Added support for package-level queries (and a README)
I noticed that package-level queries were not supported, with a vague note about
how Gerrit supports arbitrarily deep repos making this impossible. It's actually
pretty simple to support this: you just find the package with the greatest depth
that matches the request.
If you have a Gerrit setup with packages that have overlapping names (for example,
you have both "a" and "a/b" projects at the same time), then when a request for
package "a/b" is made, repo "a/b" will be preferred over "a" (even if "a" actually
had a package "b"). Just because Gerrit allows you to do crazy things with your
names doesn't mean that you should actually take it up on that offer.
Anyway, we're using this now in production and it has made it so that `go dep` works
with our private Gerrit instance.
Change-Id: I5c5daa4984a5993aec39da60ea8cca2d3ab94415
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2ede74f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+This plugin allows you to perform `go get` requests against a Gerrit project.
+
+For more information, see: `src/main/resources/Documentation/about.md`
+
diff --git a/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java b/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
index e7d50ab..871740e 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
@@ -93,15 +93,40 @@
String goGet = req.getParameter("go-get");
if ("1".equals(goGet)) {
String projectName = getProjectName(servletPath);
+ // Because Gerrit allows for arbitrary-depth project names
+ // (that is, both "a" and "a/b/c" are both legal), we are going
+ // to find the most specific such project that matches the path.
+ //
+ // For example, assume that we have the following projects:
+ // a
+ // a/b
+ // 1. If the requested path is "a", then project "a" would be chosen.
+ // 2. If the requested path is "a/b", then project "a/b" would be chosen.
+ // 3. If the requested path is "a/c", then project "a" would be chosen.
+ // 4. If the requested path is "a/b/c/d", then project "a/b" would be chosen.
+ // 5. If the requested path is "x/y/z", then this will fail with a 404 error.
+ String[] pathParts = projectName.split("/");
byte[] tosend = PAGE_404.getBytes();
rsp.setStatus(404);
- if (projectExists(projectName)) {
- tosend = PAGE_200.replace("${content}", getContent(projectName))
- .getBytes();
- rsp.setStatus(200);
- }
+ // Start with the longest-length project name first.
+ for( int length = pathParts.length; length > 0; length-- ) {
+ // Create a new project name of the specified length; each time that we
+ // go through this loop, the project name will become shorter and shorter.
+ projectName = "";
+ for( int i = 0; i < length; i++ ) {
+ if( i > 0 ) {
+ projectName += "/";
+ }
+ projectName += pathParts[i];
+ }
+ if (projectExists(projectName)) {
+ tosend = PAGE_200.replace("${content}", getContent(projectName)).getBytes();
+ rsp.setStatus(200);
+ break;
+ }
+ }
CacheHeaders.setNotCacheable(rsp);
rsp.setContentType("text/html");
rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 41a5908..d2b86eb 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,6 +1,7 @@
This plugin provides support of
[remote import paths](https://golang.org/cmd/go/#hdr-Remote_import_paths) and
-[go-get command](https://golang.org/cmd/go/).
+[go-get command](https://golang.org/cmd/go/), which means that you can perform
+`go get` (and thus `dep`) requests against a Gerrit project.
### Usage
@@ -16,10 +17,33 @@
import "example.org/foo"
```
-### Limitation
-Folder of the repository is not supported in the context of Gerrit up to
-ambiguity of repository name. E.g., _example.org/foo/bar_ has two potential meanings:
+### Packages
+Go packages may be imported by specifying the package's _folder_ after the repository name.
+For example, "import github.com/bob/my-project/package1" will download the repository from
+`github.com/bob/my-project` and then include the package `package1`.
-* Folder _bar_ in repository _foo_, or
-* repository _foo/bar_.
+For Gerrit, this is still possible, but it's a little bit more ambiguous.
+On GitHub, all projects have a depth of two: (1) group and (2) repository.
+On Gerrit, any project may have an arbitrary depth.
+Thus, the following project names are valid in Gerrit:
+
+1. `bob`
+1. `bob/my-project`
+1. `bob/my-project/some-other-project`
+1. `tom`
+1. `tom/my-project`
+
+When a user requests a package via `go get`, this plugin will attempt to match the _most specific_
+project and return that.
+
+Using our previous examples of existing projects, this plugin will return the following projects
+for the given requests:
+
+| Request | Project |
+| ---------------------------------- | ---------------- |
+| `/bob` | `bob` |
+| `/bob/package1` | `bob` |
+| `/bob/my-project` | `bob/my-project` |
+| `/bob/my-project/package1` | `bob/my-project` |
+| `/bob/my-project/package1/folder2` | `bob/my-project` |
diff --git a/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java b/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
index 405628d..532997c 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
@@ -120,7 +120,7 @@
public void testDoFilterWithExistingProject() throws Exception {
when(mockRequest.getServletPath()).thenReturn("/projectName");
when(mockRequest.getParameter("go-get")).thenReturn("1");
- when(mockProjectCache.get(any(Project.NameKey.class))).thenReturn(mockProjectState);
+ when(mockProjectCache.get(new Project.NameKey("projectName"))).thenReturn(mockProjectState);
unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
verify(mockOutputStream, times(1)).write(PAGE_200.getBytes());
verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
@@ -129,6 +129,18 @@
}
@Test
+ public void testDoFilterWithExistingProjectAndPackage() throws Exception {
+ when(mockRequest.getServletPath()).thenReturn("/projectName/my/package");
+ when(mockRequest.getParameter("go-get")).thenReturn("1");
+ when(mockProjectCache.get(new Project.NameKey("projectName"))).thenReturn(mockProjectState);
+ unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
+ verify(mockOutputStream, times(1)).write(PAGE_200.getBytes());
+ verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
+ verify(mockProjectCache, times(3)).get(any(Project.NameKey.class));
+ verify(mockResponse, times(1)).setStatus(200);
+ }
+
+ @Test
public void testDoFilterWithNonExistingProject() throws Exception {
when(mockRequest.getServletPath()).thenReturn("/projectName");
when(mockRequest.getParameter("go-get")).thenReturn("1");