From 2647040d7d116a98bef907a13f1ab9ed45bfa1dd Mon Sep 17 00:00:00 2001 From: Nir Ozery Date: Fri, 15 Nov 2024 16:39:55 -0500 Subject: [PATCH 1/5] Add support for hidden branches --- api/swagger.yml | 10 + clients/java-legacy/api/openapi.yaml | 15 + clients/java-legacy/docs/BranchCreation.md | 1 + clients/java-legacy/docs/BranchesApi.md | 6 +- clients/java-legacy/pom.xml | 2 +- .../io/lakefs/clients/api/BranchesApi.java | 26 +- .../clients/api/model/BranchCreation.java | 33 +- .../lakefs/clients/api/BranchesApiTest.java | 3 +- .../clients/api/model/BranchCreationTest.java | 8 + clients/java/api/openapi.yaml | 15 + clients/java/docs/BranchCreation.md | 1 + clients/java/docs/BranchesApi.md | 5 +- .../io/lakefs/clients/sdk/BranchesApi.java | 37 ++- .../clients/sdk/model/BranchCreation.java | 32 +- .../lakefs/clients/sdk/BranchesApiTest.java | 2 + .../clients/sdk/model/BranchCreationTest.java | 8 + clients/python-legacy/docs/BranchCreation.md | 1 + clients/python-legacy/docs/BranchesApi.md | 5 +- .../lakefs_client/api/branches_api.py | 6 + .../lakefs_client/model/branch_creation.py | 4 + clients/python/docs/BranchCreation.md | 1 + clients/python/docs/BranchesApi.md | 6 +- clients/python/lakefs_sdk/api/branches_api.py | 20 +- .../lakefs_sdk/models/branch_creation.py | 6 +- clients/python/test/test_branch_creation.py | 3 +- clients/rust/docs/BranchCreation.md | 1 + clients/rust/docs/BranchesApi.md | 3 +- clients/rust/src/apis/branches_api.rs | 5 +- clients/rust/src/models/branch_creation.rs | 4 + docs/assets/js/swagger.yml | 10 + pkg/api/controller.go | 18 +- pkg/api/controller_test.go | 301 +++++++++-------- pkg/catalog/catalog.go | 4 +- pkg/catalog/fake_graveler_test.go | 2 +- pkg/catalog/gc_write_uncommitted.go | 2 +- pkg/graveler/graveler.go | 43 ++- pkg/graveler/graveler.pb.go | 313 +++++++++--------- pkg/graveler/graveler.proto | 1 + pkg/graveler/mock/graveler.go | 21 +- pkg/graveler/ref/branch_iterator.go | 50 +-- pkg/graveler/ref/branch_iterator_test.go | 6 +- pkg/graveler/ref/manager.go | 10 +- pkg/graveler/ref/manager_test.go | 116 +++++-- pkg/graveler/testutil/fakes.go | 2 +- 44 files changed, 754 insertions(+), 414 deletions(-) diff --git a/api/swagger.yml b/api/swagger.yml index 0c59b35be6f..3c7810590a5 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -693,6 +693,10 @@ components: force: type: boolean default: false + hidden: + type: boolean + description: When set, branch will not show up when listing branches by default + default: false TagCreation: type: object @@ -3544,6 +3548,12 @@ paths: - $ref: "#/components/parameters/PaginationPrefix" - $ref: "#/components/parameters/PaginationAfter" - $ref: "#/components/parameters/PaginationAmount" + - in: query + name: show_hidden + schema: + type: boolean + default: false + description: When set - list all branches including hidden branches responses: 200: description: branch list diff --git a/clients/java-legacy/api/openapi.yaml b/clients/java-legacy/api/openapi.yaml index 50741121d4a..308ff3d647a 100644 --- a/clients/java-legacy/api/openapi.yaml +++ b/clients/java-legacy/api/openapi.yaml @@ -3115,6 +3115,15 @@ paths: minimum: -1 type: integer style: form + - explode: true + in: query + name: show_hidden + required: false + schema: + default: false + description: When set - list all branches including hidden branches + type: boolean + style: form responses: "200": content: @@ -8248,6 +8257,7 @@ components: type: object BranchCreation: example: + hidden: false name: name force: false source: source @@ -8259,6 +8269,11 @@ components: force: default: false type: boolean + hidden: + default: false + description: When set, branch will not show up when listing branches by + default + type: boolean required: - name - source diff --git a/clients/java-legacy/docs/BranchCreation.md b/clients/java-legacy/docs/BranchCreation.md index 4b4d50285c5..df8885fd95c 100644 --- a/clients/java-legacy/docs/BranchCreation.md +++ b/clients/java-legacy/docs/BranchCreation.md @@ -10,6 +10,7 @@ Name | Type | Description | Notes **name** | **String** | | **source** | **String** | | **force** | **Boolean** | | [optional] +**hidden** | **Boolean** | When set, branch will not show up when listing branches by default | [optional] diff --git a/clients/java-legacy/docs/BranchesApi.md b/clients/java-legacy/docs/BranchesApi.md index c1a1fc02597..c48e88acd89 100644 --- a/clients/java-legacy/docs/BranchesApi.md +++ b/clients/java-legacy/docs/BranchesApi.md @@ -504,7 +504,7 @@ Name | Type | Description | Notes # **listBranches** -> RefList listBranches(repository, prefix, after, amount) +> RefList listBranches(repository, prefix, after, amount, showHidden) list branches @@ -555,8 +555,9 @@ public class Example { String prefix = "prefix_example"; // String | return items prefixed with this value String after = "after_example"; // String | return items after this value Integer amount = 100; // Integer | how many items to return + Boolean showHidden = false; // Boolean | try { - RefList result = apiInstance.listBranches(repository, prefix, after, amount); + RefList result = apiInstance.listBranches(repository, prefix, after, amount, showHidden); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling BranchesApi#listBranches"); @@ -577,6 +578,7 @@ Name | Type | Description | Notes **prefix** | **String**| return items prefixed with this value | [optional] **after** | **String**| return items after this value | [optional] **amount** | **Integer**| how many items to return | [optional] [default to 100] + **showHidden** | **Boolean**| | [optional] [default to false] ### Return type diff --git a/clients/java-legacy/pom.xml b/clients/java-legacy/pom.xml index a1fdfc0ca2b..bec16246a3a 100644 --- a/clients/java-legacy/pom.xml +++ b/clients/java-legacy/pom.xml @@ -7,7 +7,7 @@ api-client 0.1.0-SNAPSHOT https://lakefs.io - [EOL] Do NOT use: lakeFS OpenAPI Java client legacy SDK + lakeFS OpenAPI Java client legacy SDK scm:git:git@github.com:treeverse/lakeFS.git scm:git:git@github.com:treeverse/lakeFS.git diff --git a/clients/java-legacy/src/main/java/io/lakefs/clients/api/BranchesApi.java b/clients/java-legacy/src/main/java/io/lakefs/clients/api/BranchesApi.java index a72c0b7cf50..e07f3713cab 100644 --- a/clients/java-legacy/src/main/java/io/lakefs/clients/api/BranchesApi.java +++ b/clients/java-legacy/src/main/java/io/lakefs/clients/api/BranchesApi.java @@ -830,6 +830,7 @@ public okhttp3.Call getBranchAsync(String repository, String branch, final ApiCa * @param prefix return items prefixed with this value (optional) * @param after return items after this value (optional) * @param amount how many items to return (optional, default to 100) + * @param showHidden (optional, default to false) * @param _callback Callback for upload/download progress * @return Call to execute * @throws ApiException If fail to serialize the request body object @@ -843,7 +844,7 @@ public okhttp3.Call getBranchAsync(String repository, String branch, final ApiCa 0 Internal Server Error - */ - public okhttp3.Call listBranchesCall(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + public okhttp3.Call listBranchesCall(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { Object localVarPostBody = null; // create path and map variables @@ -868,6 +869,10 @@ public okhttp3.Call listBranchesCall(String repository, String prefix, String af localVarQueryParams.addAll(localVarApiClient.parameterToPair("amount", amount)); } + if (showHidden != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("show_hidden", showHidden)); + } + final String[] localVarAccepts = { "application/json" }; @@ -887,7 +892,7 @@ public okhttp3.Call listBranchesCall(String repository, String prefix, String af } @SuppressWarnings("rawtypes") - private okhttp3.Call listBranchesValidateBeforeCall(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + private okhttp3.Call listBranchesValidateBeforeCall(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { // verify the required parameter 'repository' is set if (repository == null) { @@ -895,7 +900,7 @@ private okhttp3.Call listBranchesValidateBeforeCall(String repository, String pr } - okhttp3.Call localVarCall = listBranchesCall(repository, prefix, after, amount, _callback); + okhttp3.Call localVarCall = listBranchesCall(repository, prefix, after, amount, showHidden, _callback); return localVarCall; } @@ -907,6 +912,7 @@ private okhttp3.Call listBranchesValidateBeforeCall(String repository, String pr * @param prefix return items prefixed with this value (optional) * @param after return items after this value (optional) * @param amount how many items to return (optional, default to 100) + * @param showHidden (optional, default to false) * @return RefList * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @http.response.details @@ -919,8 +925,8 @@ private okhttp3.Call listBranchesValidateBeforeCall(String repository, String pr 0 Internal Server Error - */ - public RefList listBranches(String repository, String prefix, String after, Integer amount) throws ApiException { - ApiResponse localVarResp = listBranchesWithHttpInfo(repository, prefix, after, amount); + public RefList listBranches(String repository, String prefix, String after, Integer amount, Boolean showHidden) throws ApiException { + ApiResponse localVarResp = listBranchesWithHttpInfo(repository, prefix, after, amount, showHidden); return localVarResp.getData(); } @@ -931,6 +937,7 @@ public RefList listBranches(String repository, String prefix, String after, Inte * @param prefix return items prefixed with this value (optional) * @param after return items after this value (optional) * @param amount how many items to return (optional, default to 100) + * @param showHidden (optional, default to false) * @return ApiResponse<RefList> * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @http.response.details @@ -943,8 +950,8 @@ public RefList listBranches(String repository, String prefix, String after, Inte 0 Internal Server Error - */ - public ApiResponse listBranchesWithHttpInfo(String repository, String prefix, String after, Integer amount) throws ApiException { - okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, null); + public ApiResponse listBranchesWithHttpInfo(String repository, String prefix, String after, Integer amount, Boolean showHidden) throws ApiException { + okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, showHidden, null); Type localVarReturnType = new TypeToken(){}.getType(); return localVarApiClient.execute(localVarCall, localVarReturnType); } @@ -956,6 +963,7 @@ public ApiResponse listBranchesWithHttpInfo(String repository, String p * @param prefix return items prefixed with this value (optional) * @param after return items after this value (optional) * @param amount how many items to return (optional, default to 100) + * @param showHidden (optional, default to false) * @param _callback The callback to be executed when the API call finishes * @return The request call * @throws ApiException If fail to process the API call, e.g. serializing the request body object @@ -969,9 +977,9 @@ public ApiResponse listBranchesWithHttpInfo(String repository, String p 0 Internal Server Error - */ - public okhttp3.Call listBranchesAsync(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + public okhttp3.Call listBranchesAsync(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, _callback); + okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, showHidden, _callback); Type localVarReturnType = new TypeToken(){}.getType(); localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; diff --git a/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java b/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java index 3257bad1fcc..bc5a6805908 100644 --- a/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java +++ b/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java @@ -41,6 +41,10 @@ public class BranchCreation { @SerializedName(SERIALIZED_NAME_FORCE) private Boolean force = false; + public static final String SERIALIZED_NAME_HIDDEN = "hidden"; + @SerializedName(SERIALIZED_NAME_HIDDEN) + private Boolean hidden = false; + public BranchCreation name(String name) { @@ -111,6 +115,29 @@ public void setForce(Boolean force) { } + public BranchCreation hidden(Boolean hidden) { + + this.hidden = hidden; + return this; + } + + /** + * When set, branch will not show up when listing branches by default + * @return hidden + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "When set, branch will not show up when listing branches by default") + + public Boolean getHidden() { + return hidden; + } + + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + + @Override public boolean equals(Object o) { if (this == o) { @@ -122,12 +149,13 @@ public boolean equals(Object o) { BranchCreation branchCreation = (BranchCreation) o; return Objects.equals(this.name, branchCreation.name) && Objects.equals(this.source, branchCreation.source) && - Objects.equals(this.force, branchCreation.force); + Objects.equals(this.force, branchCreation.force) && + Objects.equals(this.hidden, branchCreation.hidden); } @Override public int hashCode() { - return Objects.hash(name, source, force); + return Objects.hash(name, source, force, hidden); } @Override @@ -137,6 +165,7 @@ public String toString() { sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" source: ").append(toIndentedString(source)).append("\n"); sb.append(" force: ").append(toIndentedString(force)).append("\n"); + sb.append(" hidden: ").append(toIndentedString(hidden)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/clients/java-legacy/src/test/java/io/lakefs/clients/api/BranchesApiTest.java b/clients/java-legacy/src/test/java/io/lakefs/clients/api/BranchesApiTest.java index 7e7b1109f61..91f47a689fe 100644 --- a/clients/java-legacy/src/test/java/io/lakefs/clients/api/BranchesApiTest.java +++ b/clients/java-legacy/src/test/java/io/lakefs/clients/api/BranchesApiTest.java @@ -140,7 +140,8 @@ public void listBranchesTest() throws ApiException { String prefix = null; String after = null; Integer amount = null; - RefList response = api.listBranches(repository, prefix, after, amount); + Boolean showHidden = null; + RefList response = api.listBranches(repository, prefix, after, amount, showHidden); // TODO: test validations } diff --git a/clients/java-legacy/src/test/java/io/lakefs/clients/api/model/BranchCreationTest.java b/clients/java-legacy/src/test/java/io/lakefs/clients/api/model/BranchCreationTest.java index fdcfef6b6a2..b08ce8cf27d 100644 --- a/clients/java-legacy/src/test/java/io/lakefs/clients/api/model/BranchCreationTest.java +++ b/clients/java-legacy/src/test/java/io/lakefs/clients/api/model/BranchCreationTest.java @@ -64,4 +64,12 @@ public void forceTest() { // TODO: test force } + /** + * Test the property 'hidden' + */ + @Test + public void hiddenTest() { + // TODO: test hidden + } + } diff --git a/clients/java/api/openapi.yaml b/clients/java/api/openapi.yaml index 0b1700b5e61..690ac3f1692 100644 --- a/clients/java/api/openapi.yaml +++ b/clients/java/api/openapi.yaml @@ -3115,6 +3115,15 @@ paths: minimum: -1 type: integer style: form + - explode: true + in: query + name: show_hidden + required: false + schema: + default: false + description: When set - list all branches including hidden branches + type: boolean + style: form responses: "200": content: @@ -8222,6 +8231,7 @@ components: type: object BranchCreation: example: + hidden: false name: name force: false source: source @@ -8233,6 +8243,11 @@ components: force: default: false type: boolean + hidden: + default: false + description: "When set, branch will not show up when listing branches by\ + \ default" + type: boolean required: - name - source diff --git a/clients/java/docs/BranchCreation.md b/clients/java/docs/BranchCreation.md index 73665aab256..305cb2b84d1 100644 --- a/clients/java/docs/BranchCreation.md +++ b/clients/java/docs/BranchCreation.md @@ -10,6 +10,7 @@ |**name** | **String** | | | |**source** | **String** | | | |**force** | **Boolean** | | [optional] | +|**hidden** | **Boolean** | When set, branch will not show up when listing branches by default | [optional] | diff --git a/clients/java/docs/BranchesApi.md b/clients/java/docs/BranchesApi.md index 7f46857e87b..f010c4e0b07 100644 --- a/clients/java/docs/BranchesApi.md +++ b/clients/java/docs/BranchesApi.md @@ -514,7 +514,7 @@ public class Example { # **listBranches** -> RefList listBranches(repository).prefix(prefix).after(after).amount(amount).execute(); +> RefList listBranches(repository).prefix(prefix).after(after).amount(amount).showHidden(showHidden).execute(); list branches @@ -565,11 +565,13 @@ public class Example { String prefix = "prefix_example"; // String | return items prefixed with this value String after = "after_example"; // String | return items after this value Integer amount = 100; // Integer | how many items to return + Boolean showHidden = false; // Boolean | try { RefList result = apiInstance.listBranches(repository) .prefix(prefix) .after(after) .amount(amount) + .showHidden(showHidden) .execute(); System.out.println(result); } catch (ApiException e) { @@ -591,6 +593,7 @@ public class Example { | **prefix** | **String**| return items prefixed with this value | [optional] | | **after** | **String**| return items after this value | [optional] | | **amount** | **Integer**| how many items to return | [optional] [default to 100] | +| **showHidden** | **Boolean**| | [optional] [default to false] | ### Return type diff --git a/clients/java/src/main/java/io/lakefs/clients/sdk/BranchesApi.java b/clients/java/src/main/java/io/lakefs/clients/sdk/BranchesApi.java index 4a78a796eca..881f8670c28 100644 --- a/clients/java/src/main/java/io/lakefs/clients/sdk/BranchesApi.java +++ b/clients/java/src/main/java/io/lakefs/clients/sdk/BranchesApi.java @@ -1101,7 +1101,7 @@ public okhttp3.Call executeAsync(final ApiCallback _callback) throws ApiExc public APIgetBranchRequest getBranch(String repository, String branch) { return new APIgetBranchRequest(repository, branch); } - private okhttp3.Call listBranchesCall(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + private okhttp3.Call listBranchesCall(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -1139,6 +1139,10 @@ private okhttp3.Call listBranchesCall(String repository, String prefix, String a localVarQueryParams.addAll(localVarApiClient.parameterToPair("amount", amount)); } + if (showHidden != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("show_hidden", showHidden)); + } + final String[] localVarAccepts = { "application/json" }; @@ -1159,26 +1163,26 @@ private okhttp3.Call listBranchesCall(String repository, String prefix, String a } @SuppressWarnings("rawtypes") - private okhttp3.Call listBranchesValidateBeforeCall(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + private okhttp3.Call listBranchesValidateBeforeCall(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { // verify the required parameter 'repository' is set if (repository == null) { throw new ApiException("Missing the required parameter 'repository' when calling listBranches(Async)"); } - return listBranchesCall(repository, prefix, after, amount, _callback); + return listBranchesCall(repository, prefix, after, amount, showHidden, _callback); } - private ApiResponse listBranchesWithHttpInfo(String repository, String prefix, String after, Integer amount) throws ApiException { - okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, null); + private ApiResponse listBranchesWithHttpInfo(String repository, String prefix, String after, Integer amount, Boolean showHidden) throws ApiException { + okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, showHidden, null); Type localVarReturnType = new TypeToken(){}.getType(); return localVarApiClient.execute(localVarCall, localVarReturnType); } - private okhttp3.Call listBranchesAsync(String repository, String prefix, String after, Integer amount, final ApiCallback _callback) throws ApiException { + private okhttp3.Call listBranchesAsync(String repository, String prefix, String after, Integer amount, Boolean showHidden, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, _callback); + okhttp3.Call localVarCall = listBranchesValidateBeforeCall(repository, prefix, after, amount, showHidden, _callback); Type localVarReturnType = new TypeToken(){}.getType(); localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; @@ -1189,6 +1193,7 @@ public class APIlistBranchesRequest { private String prefix; private String after; private Integer amount; + private Boolean showHidden; private APIlistBranchesRequest(String repository) { this.repository = repository; @@ -1224,6 +1229,16 @@ public APIlistBranchesRequest amount(Integer amount) { return this; } + /** + * Set showHidden + * @param showHidden (optional, default to false) + * @return APIlistBranchesRequest + */ + public APIlistBranchesRequest showHidden(Boolean showHidden) { + this.showHidden = showHidden; + return this; + } + /** * Build call for listBranches * @param _callback ApiCallback API callback @@ -1240,7 +1255,7 @@ public APIlistBranchesRequest amount(Integer amount) { */ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { - return listBranchesCall(repository, prefix, after, amount, _callback); + return listBranchesCall(repository, prefix, after, amount, showHidden, _callback); } /** @@ -1258,7 +1273,7 @@ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { */ public RefList execute() throws ApiException { - ApiResponse localVarResp = listBranchesWithHttpInfo(repository, prefix, after, amount); + ApiResponse localVarResp = listBranchesWithHttpInfo(repository, prefix, after, amount, showHidden); return localVarResp.getData(); } @@ -1277,7 +1292,7 @@ public RefList execute() throws ApiException { */ public ApiResponse executeWithHttpInfo() throws ApiException { - return listBranchesWithHttpInfo(repository, prefix, after, amount); + return listBranchesWithHttpInfo(repository, prefix, after, amount, showHidden); } /** @@ -1296,7 +1311,7 @@ public ApiResponse executeWithHttpInfo() throws ApiException { */ public okhttp3.Call executeAsync(final ApiCallback _callback) throws ApiException { - return listBranchesAsync(repository, prefix, after, amount, _callback); + return listBranchesAsync(repository, prefix, after, amount, showHidden, _callback); } } diff --git a/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java b/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java index 1b80f7fdbac..57f97626b02 100644 --- a/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java +++ b/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java @@ -64,6 +64,10 @@ public class BranchCreation { @SerializedName(SERIALIZED_NAME_FORCE) private Boolean force = false; + public static final String SERIALIZED_NAME_HIDDEN = "hidden"; + @SerializedName(SERIALIZED_NAME_HIDDEN) + private Boolean hidden = false; + public BranchCreation() { } @@ -129,6 +133,27 @@ public void setForce(Boolean force) { this.force = force; } + + public BranchCreation hidden(Boolean hidden) { + + this.hidden = hidden; + return this; + } + + /** + * When set, branch will not show up when listing branches by default + * @return hidden + **/ + @javax.annotation.Nullable + public Boolean getHidden() { + return hidden; + } + + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + /** * A container for additional, undeclared properties. * This is a holder for any undeclared properties as specified with @@ -186,13 +211,14 @@ public boolean equals(Object o) { BranchCreation branchCreation = (BranchCreation) o; return Objects.equals(this.name, branchCreation.name) && Objects.equals(this.source, branchCreation.source) && - Objects.equals(this.force, branchCreation.force)&& + Objects.equals(this.force, branchCreation.force) && + Objects.equals(this.hidden, branchCreation.hidden)&& Objects.equals(this.additionalProperties, branchCreation.additionalProperties); } @Override public int hashCode() { - return Objects.hash(name, source, force, additionalProperties); + return Objects.hash(name, source, force, hidden, additionalProperties); } @Override @@ -202,6 +228,7 @@ public String toString() { sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" source: ").append(toIndentedString(source)).append("\n"); sb.append(" force: ").append(toIndentedString(force)).append("\n"); + sb.append(" hidden: ").append(toIndentedString(hidden)).append("\n"); sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); sb.append("}"); return sb.toString(); @@ -228,6 +255,7 @@ private String toIndentedString(Object o) { openapiFields.add("name"); openapiFields.add("source"); openapiFields.add("force"); + openapiFields.add("hidden"); // a set of required properties/fields (JSON key names) openapiRequiredFields = new HashSet(); diff --git a/clients/java/src/test/java/io/lakefs/clients/sdk/BranchesApiTest.java b/clients/java/src/test/java/io/lakefs/clients/sdk/BranchesApiTest.java index e296008c9b5..94a8c4aef87 100644 --- a/clients/java/src/test/java/io/lakefs/clients/sdk/BranchesApiTest.java +++ b/clients/java/src/test/java/io/lakefs/clients/sdk/BranchesApiTest.java @@ -131,10 +131,12 @@ public void listBranchesTest() throws ApiException { String prefix = null; String after = null; Integer amount = null; + Boolean showHidden = null; RefList response = api.listBranches(repository) .prefix(prefix) .after(after) .amount(amount) + .showHidden(showHidden) .execute(); // TODO: test validations } diff --git a/clients/java/src/test/java/io/lakefs/clients/sdk/model/BranchCreationTest.java b/clients/java/src/test/java/io/lakefs/clients/sdk/model/BranchCreationTest.java index 6593057fb46..d6f9134de1a 100644 --- a/clients/java/src/test/java/io/lakefs/clients/sdk/model/BranchCreationTest.java +++ b/clients/java/src/test/java/io/lakefs/clients/sdk/model/BranchCreationTest.java @@ -61,4 +61,12 @@ public void forceTest() { // TODO: test force } + /** + * Test the property 'hidden' + */ + @Test + public void hiddenTest() { + // TODO: test hidden + } + } diff --git a/clients/python-legacy/docs/BranchCreation.md b/clients/python-legacy/docs/BranchCreation.md index 7af7f161dbb..d5dab1c5619 100644 --- a/clients/python-legacy/docs/BranchCreation.md +++ b/clients/python-legacy/docs/BranchCreation.md @@ -7,6 +7,7 @@ Name | Type | Description | Notes **name** | **str** | | **source** | **str** | | **force** | **bool** | | [optional] if omitted the server will use the default value of False +**hidden** | **bool** | When set, branch will not show up when listing branches by default | [optional] if omitted the server will use the default value of False **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/clients/python-legacy/docs/BranchesApi.md b/clients/python-legacy/docs/BranchesApi.md index 24f4c8f7fcd..23c1b7c5695 100644 --- a/clients/python-legacy/docs/BranchesApi.md +++ b/clients/python-legacy/docs/BranchesApi.md @@ -209,6 +209,7 @@ with lakefs_client.ApiClient(configuration) as api_client: name="name_example", source="source_example", force=False, + hidden=False, ) # BranchCreation | # example passing only required values which don't have defaults set @@ -681,6 +682,7 @@ with lakefs_client.ApiClient(configuration) as api_client: prefix = "prefix_example" # str | return items prefixed with this value (optional) after = "after_example" # str | return items after this value (optional) amount = 100 # int | how many items to return (optional) if omitted the server will use the default value of 100 + show_hidden = False # bool | (optional) if omitted the server will use the default value of False # example passing only required values which don't have defaults set try: @@ -694,7 +696,7 @@ with lakefs_client.ApiClient(configuration) as api_client: # and optional values try: # list branches - api_response = api_instance.list_branches(repository, prefix=prefix, after=after, amount=amount) + api_response = api_instance.list_branches(repository, prefix=prefix, after=after, amount=amount, show_hidden=show_hidden) pprint(api_response) except lakefs_client.ApiException as e: print("Exception when calling BranchesApi->list_branches: %s\n" % e) @@ -709,6 +711,7 @@ Name | Type | Description | Notes **prefix** | **str**| return items prefixed with this value | [optional] **after** | **str**| return items after this value | [optional] **amount** | **int**| how many items to return | [optional] if omitted the server will use the default value of 100 + **show_hidden** | **bool**| | [optional] if omitted the server will use the default value of False ### Return type diff --git a/clients/python-legacy/lakefs_client/api/branches_api.py b/clients/python-legacy/lakefs_client/api/branches_api.py index aa2a208efd5..c9fb8b43423 100644 --- a/clients/python-legacy/lakefs_client/api/branches_api.py +++ b/clients/python-legacy/lakefs_client/api/branches_api.py @@ -410,6 +410,7 @@ def __init__(self, api_client=None): 'prefix', 'after', 'amount', + 'show_hidden', ], 'required': [ 'repository', @@ -441,18 +442,22 @@ def __init__(self, api_client=None): (str,), 'amount': (int,), + 'show_hidden': + (bool,), }, 'attribute_map': { 'repository': 'repository', 'prefix': 'prefix', 'after': 'after', 'amount': 'amount', + 'show_hidden': 'show_hidden', }, 'location_map': { 'repository': 'path', 'prefix': 'query', 'after': 'query', 'amount': 'query', + 'show_hidden': 'query', }, 'collection_format_map': { } @@ -976,6 +981,7 @@ def list_branches( prefix (str): return items prefixed with this value. [optional] after (str): return items after this value. [optional] amount (int): how many items to return. [optional] if omitted the server will use the default value of 100 + show_hidden (bool): [optional] if omitted the server will use the default value of False _return_http_data_only (bool): response data without head status code and headers. Default is True. _preload_content (bool): if False, the urllib3.HTTPResponse object diff --git a/clients/python-legacy/lakefs_client/model/branch_creation.py b/clients/python-legacy/lakefs_client/model/branch_creation.py index 3e54f460966..51af3d1356f 100644 --- a/clients/python-legacy/lakefs_client/model/branch_creation.py +++ b/clients/python-legacy/lakefs_client/model/branch_creation.py @@ -85,6 +85,7 @@ def openapi_types(): 'name': (str,), # noqa: E501 'source': (str,), # noqa: E501 'force': (bool,), # noqa: E501 + 'hidden': (bool,), # noqa: E501 } @cached_property @@ -96,6 +97,7 @@ def discriminator(): 'name': 'name', # noqa: E501 'source': 'source', # noqa: E501 'force': 'force', # noqa: E501 + 'hidden': 'hidden', # noqa: E501 } read_only_vars = { @@ -144,6 +146,7 @@ def _from_openapi_data(cls, name, source, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) force (bool): [optional] if omitted the server will use the default value of False # noqa: E501 + hidden (bool): When set, branch will not show up when listing branches by default. [optional] if omitted the server will use the default value of False # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -232,6 +235,7 @@ def __init__(self, name, source, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) force (bool): [optional] if omitted the server will use the default value of False # noqa: E501 + hidden (bool): When set, branch will not show up when listing branches by default. [optional] if omitted the server will use the default value of False # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) diff --git a/clients/python/docs/BranchCreation.md b/clients/python/docs/BranchCreation.md index fc2c6620e9e..6be9e382d83 100644 --- a/clients/python/docs/BranchCreation.md +++ b/clients/python/docs/BranchCreation.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes **name** | **str** | | **source** | **str** | | **force** | **bool** | | [optional] [default to False] +**hidden** | **bool** | When set, branch will not show up when listing branches by default | [optional] [default to False] ## Example diff --git a/clients/python/docs/BranchesApi.md b/clients/python/docs/BranchesApi.md index 33c09474b8f..062738f387a 100644 --- a/clients/python/docs/BranchesApi.md +++ b/clients/python/docs/BranchesApi.md @@ -592,7 +592,7 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **list_branches** -> RefList list_branches(repository, prefix=prefix, after=after, amount=amount) +> RefList list_branches(repository, prefix=prefix, after=after, amount=amount, show_hidden=show_hidden) list branches @@ -660,10 +660,11 @@ with lakefs_sdk.ApiClient(configuration) as api_client: prefix = 'prefix_example' # str | return items prefixed with this value (optional) after = 'after_example' # str | return items after this value (optional) amount = 100 # int | how many items to return (optional) (default to 100) + show_hidden = False # bool | (optional) (default to False) try: # list branches - api_response = api_instance.list_branches(repository, prefix=prefix, after=after, amount=amount) + api_response = api_instance.list_branches(repository, prefix=prefix, after=after, amount=amount, show_hidden=show_hidden) print("The response of BranchesApi->list_branches:\n") pprint(api_response) except Exception as e: @@ -681,6 +682,7 @@ Name | Type | Description | Notes **prefix** | **str**| return items prefixed with this value | [optional] **after** | **str**| return items after this value | [optional] **amount** | **int**| how many items to return | [optional] [default to 100] + **show_hidden** | **bool**| | [optional] [default to False] ### Return type diff --git a/clients/python/lakefs_sdk/api/branches_api.py b/clients/python/lakefs_sdk/api/branches_api.py index b17a83d0342..734716c67fa 100644 --- a/clients/python/lakefs_sdk/api/branches_api.py +++ b/clients/python/lakefs_sdk/api/branches_api.py @@ -863,13 +863,13 @@ def get_branch_with_http_info(self, repository : StrictStr, branch : StrictStr, _request_auth=_params.get('_request_auth')) @validate_arguments - def list_branches(self, repository : StrictStr, prefix : Annotated[Optional[StrictStr], Field(description="return items prefixed with this value")] = None, after : Annotated[Optional[StrictStr], Field(description="return items after this value")] = None, amount : Annotated[Optional[conint(strict=True, le=1000, ge=-1)], Field(description="how many items to return")] = None, **kwargs) -> RefList: # noqa: E501 + def list_branches(self, repository : StrictStr, prefix : Annotated[Optional[StrictStr], Field(description="return items prefixed with this value")] = None, after : Annotated[Optional[StrictStr], Field(description="return items after this value")] = None, amount : Annotated[Optional[conint(strict=True, le=1000, ge=-1)], Field(description="how many items to return")] = None, show_hidden : Optional[StrictBool] = None, **kwargs) -> RefList: # noqa: E501 """list branches # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.list_branches(repository, prefix, after, amount, async_req=True) + >>> thread = api.list_branches(repository, prefix, after, amount, show_hidden, async_req=True) >>> result = thread.get() :param repository: (required) @@ -880,6 +880,8 @@ def list_branches(self, repository : StrictStr, prefix : Annotated[Optional[Stri :type after: str :param amount: how many items to return :type amount: int + :param show_hidden: + :type show_hidden: bool :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _request_timeout: timeout setting for this request. If one @@ -894,16 +896,16 @@ def list_branches(self, repository : StrictStr, prefix : Annotated[Optional[Stri kwargs['_return_http_data_only'] = True if '_preload_content' in kwargs: raise ValueError("Error! Please call the list_branches_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") - return self.list_branches_with_http_info(repository, prefix, after, amount, **kwargs) # noqa: E501 + return self.list_branches_with_http_info(repository, prefix, after, amount, show_hidden, **kwargs) # noqa: E501 @validate_arguments - def list_branches_with_http_info(self, repository : StrictStr, prefix : Annotated[Optional[StrictStr], Field(description="return items prefixed with this value")] = None, after : Annotated[Optional[StrictStr], Field(description="return items after this value")] = None, amount : Annotated[Optional[conint(strict=True, le=1000, ge=-1)], Field(description="how many items to return")] = None, **kwargs) -> ApiResponse: # noqa: E501 + def list_branches_with_http_info(self, repository : StrictStr, prefix : Annotated[Optional[StrictStr], Field(description="return items prefixed with this value")] = None, after : Annotated[Optional[StrictStr], Field(description="return items after this value")] = None, amount : Annotated[Optional[conint(strict=True, le=1000, ge=-1)], Field(description="how many items to return")] = None, show_hidden : Optional[StrictBool] = None, **kwargs) -> ApiResponse: # noqa: E501 """list branches # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.list_branches_with_http_info(repository, prefix, after, amount, async_req=True) + >>> thread = api.list_branches_with_http_info(repository, prefix, after, amount, show_hidden, async_req=True) >>> result = thread.get() :param repository: (required) @@ -914,6 +916,8 @@ def list_branches_with_http_info(self, repository : StrictStr, prefix : Annotate :type after: str :param amount: how many items to return :type amount: int + :param show_hidden: + :type show_hidden: bool :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _preload_content: if False, the ApiResponse.data will @@ -945,7 +949,8 @@ def list_branches_with_http_info(self, repository : StrictStr, prefix : Annotate 'repository', 'prefix', 'after', - 'amount' + 'amount', + 'show_hidden' ] _all_params.extend( [ @@ -988,6 +993,9 @@ def list_branches_with_http_info(self, repository : StrictStr, prefix : Annotate if _params.get('amount') is not None: # noqa: E501 _query_params.append(('amount', _params['amount'])) + if _params.get('show_hidden') is not None: # noqa: E501 + _query_params.append(('show_hidden', _params['show_hidden'])) + # process the header parameters _header_params = dict(_params.get('_headers', {})) # process the form parameters diff --git a/clients/python/lakefs_sdk/models/branch_creation.py b/clients/python/lakefs_sdk/models/branch_creation.py index e9db41e4bea..679c2353274 100644 --- a/clients/python/lakefs_sdk/models/branch_creation.py +++ b/clients/python/lakefs_sdk/models/branch_creation.py @@ -32,7 +32,8 @@ class BranchCreation(BaseModel): name: StrictStr = Field(...) source: StrictStr = Field(...) force: Optional[StrictBool] = False - __properties = ["name", "source", "force"] + hidden: Optional[StrictBool] = Field(False, description="When set, branch will not show up when listing branches by default") + __properties = ["name", "source", "force", "hidden"] class Config: """Pydantic configuration""" @@ -72,7 +73,8 @@ def from_dict(cls, obj: dict) -> BranchCreation: _obj = BranchCreation.parse_obj({ "name": obj.get("name"), "source": obj.get("source"), - "force": obj.get("force") if obj.get("force") is not None else False + "force": obj.get("force") if obj.get("force") is not None else False, + "hidden": obj.get("hidden") if obj.get("hidden") is not None else False }) return _obj diff --git a/clients/python/test/test_branch_creation.py b/clients/python/test/test_branch_creation.py index 6d5842f8876..8798d44e20b 100644 --- a/clients/python/test/test_branch_creation.py +++ b/clients/python/test/test_branch_creation.py @@ -41,7 +41,8 @@ def make_instance(self, include_optional): return BranchCreation( name = '', source = '', - force = True + force = True, + hidden = True ) else : return BranchCreation( diff --git a/clients/rust/docs/BranchCreation.md b/clients/rust/docs/BranchCreation.md index 4f17664ba99..c08b639c9df 100644 --- a/clients/rust/docs/BranchCreation.md +++ b/clients/rust/docs/BranchCreation.md @@ -7,6 +7,7 @@ Name | Type | Description | Notes **name** | **String** | | **source** | **String** | | **force** | Option<**bool**> | | [optional][default to false] +**hidden** | Option<**bool**> | When set, branch will not show up when listing branches by default | [optional][default to false] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/clients/rust/docs/BranchesApi.md b/clients/rust/docs/BranchesApi.md index 6bd59f2bf15..ef01e17a6c2 100644 --- a/clients/rust/docs/BranchesApi.md +++ b/clients/rust/docs/BranchesApi.md @@ -168,7 +168,7 @@ Name | Type | Description | Required | Notes ## list_branches -> models::RefList list_branches(repository, prefix, after, amount) +> models::RefList list_branches(repository, prefix, after, amount, show_hidden) list branches ### Parameters @@ -180,6 +180,7 @@ Name | Type | Description | Required | Notes **prefix** | Option<**String**> | return items prefixed with this value | | **after** | Option<**String**> | return items after this value | | **amount** | Option<**i32**> | how many items to return | |[default to 100] +**show_hidden** | Option<**bool**> | | |[default to false] ### Return type diff --git a/clients/rust/src/apis/branches_api.rs b/clients/rust/src/apis/branches_api.rs index a8f8364ee74..e9a0adc657f 100644 --- a/clients/rust/src/apis/branches_api.rs +++ b/clients/rust/src/apis/branches_api.rs @@ -298,7 +298,7 @@ pub async fn get_branch(configuration: &configuration::Configuration, repository } } -pub async fn list_branches(configuration: &configuration::Configuration, repository: &str, prefix: Option<&str>, after: Option<&str>, amount: Option) -> Result> { +pub async fn list_branches(configuration: &configuration::Configuration, repository: &str, prefix: Option<&str>, after: Option<&str>, amount: Option, show_hidden: Option) -> Result> { let local_var_configuration = configuration; let local_var_client = &local_var_configuration.client; @@ -315,6 +315,9 @@ pub async fn list_branches(configuration: &configuration::Configuration, reposit if let Some(ref local_var_str) = amount { local_var_req_builder = local_var_req_builder.query(&[("amount", &local_var_str.to_string())]); } + if let Some(ref local_var_str) = show_hidden { + local_var_req_builder = local_var_req_builder.query(&[("show_hidden", &local_var_str.to_string())]); + } if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); } diff --git a/clients/rust/src/models/branch_creation.rs b/clients/rust/src/models/branch_creation.rs index 7a093ad0acf..80942b00314 100644 --- a/clients/rust/src/models/branch_creation.rs +++ b/clients/rust/src/models/branch_creation.rs @@ -18,6 +18,9 @@ pub struct BranchCreation { pub source: String, #[serde(rename = "force", skip_serializing_if = "Option::is_none")] pub force: Option, + /// When set, branch will not show up when listing branches by default + #[serde(rename = "hidden", skip_serializing_if = "Option::is_none")] + pub hidden: Option, } impl BranchCreation { @@ -26,6 +29,7 @@ impl BranchCreation { name, source, force: None, + hidden: None, } } } diff --git a/docs/assets/js/swagger.yml b/docs/assets/js/swagger.yml index 0c59b35be6f..3c7810590a5 100644 --- a/docs/assets/js/swagger.yml +++ b/docs/assets/js/swagger.yml @@ -693,6 +693,10 @@ components: force: type: boolean default: false + hidden: + type: boolean + description: When set, branch will not show up when listing branches by default + default: false TagCreation: type: object @@ -3544,6 +3548,12 @@ paths: - $ref: "#/components/parameters/PaginationPrefix" - $ref: "#/components/parameters/PaginationAfter" - $ref: "#/components/parameters/PaginationAmount" + - in: query + name: show_hidden + schema: + type: boolean + default: false + description: When set - list all branches including hidden branches responses: 200: description: branch list diff --git a/pkg/api/controller.go b/pkg/api/controller.go index 61b0c363b5b..4e3e7a34ad5 100644 --- a/pkg/api/controller.go +++ b/pkg/api/controller.go @@ -2578,8 +2578,13 @@ func (c *Controller) ListBranches(w http.ResponseWriter, r *http.Request, reposi } ctx := r.Context() c.LogAction(ctx, "list_branches", r, repository, "", "") - - res, hasMore, err := c.Catalog.ListBranches(ctx, repository, paginationPrefix(params.Prefix), paginationAmount(params.Amount), paginationAfter(params.After)) + res, hasMore, err := c.Catalog.ListBranches( + ctx, + repository, + paginationPrefix(params.Prefix), + paginationAmount(params.Amount), + paginationAfter(params.After), + graveler.WithShowHidden(swag.BoolValue(params.ShowHidden))) if c.handleAPIError(ctx, w, r, err) { return } @@ -2610,7 +2615,14 @@ func (c *Controller) CreateBranch(w http.ResponseWriter, r *http.Request, body a ctx := r.Context() c.LogAction(ctx, "create_branch", r, repository, body.Name, "") - commitLog, err := c.Catalog.CreateBranch(ctx, repository, body.Name, body.Source, graveler.WithForce(swag.BoolValue(body.Force))) + commitLog, err := c.Catalog.CreateBranch( + ctx, + repository, + body.Name, + body.Source, + graveler.WithForce(swag.BoolValue(body.Force)), + graveler.WithHidden(swag.BoolValue(body.Hidden)), + ) if c.handleAPIError(ctx, w, r, err) { return } diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index 4ef5ccf8d65..b32a952c8aa 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -1482,7 +1482,7 @@ func TestController_ListBranchesHandler(t *testing.T) { for i := 0; i < 7; i++ { branchName := "main" + strconv.Itoa(i+1) - _, err := deps.catalog.CreateBranch(ctx, repo, branchName, "main") + _, err := deps.catalog.CreateBranch(ctx, repo, branchName, "main", graveler.WithHidden(i%2 != 0)) testutil.MustDo(t, "create branch "+branchName, err) } resp, err := clt.ListBranchesWithResponse(ctx, repo, &apigen.ListBranchesParams{ @@ -1502,11 +1502,22 @@ func TestController_ListBranchesHandler(t *testing.T) { if len(results) != 2 { t.Fatalf("expected 2 branches to return, got %d", len(results)) } - retReference := results[0] - const expectedID = "main2" - if retReference.Id != expectedID { - t.Fatalf("expected '%s' as the first result for the second page, got '%s' instead", expectedID, retReference.Id) + require.Equal(t, results[0].Id, "main3") + require.Equal(t, results[1].Id, "main5") + + // List all branches + resp, err = clt.ListBranchesWithResponse(ctx, repo, &apigen.ListBranchesParams{ + After: apiutil.Ptr[apigen.PaginationAfter]("main1"), + Amount: apiutil.Ptr[apigen.PaginationAmount](2), + ShowHidden: swag.Bool(true), + }) + verifyResponseOK(t, resp, err) + results = resp.JSON200.Results + if len(results) != 2 { + t.Fatalf("expected 2 branches to return, got %d", len(results)) } + require.Equal(t, results[0].Id, "main2") + require.Equal(t, results[1].Id, "main3") }) t.Run("list branches repo doesnt exist", func(t *testing.T) { @@ -1703,156 +1714,168 @@ func TestController_BranchesDiffBranchHandler(t *testing.T) { func TestController_CreateBranchHandler(t *testing.T) { clt, deps := setupClientWithAdmin(t) ctx := context.Background() - t.Run("create branch and diff refs success", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) - testutil.Must(t, err) - testutil.Must(t, deps.catalog.CreateEntry(ctx, repo, "main", catalog.DBEntry{Path: "a/b"})) - _, err = deps.catalog.Commit(ctx, repo, "main", "first commit", "test", nil, nil, nil, false) - testutil.Must(t, err) + for _, hidden := range []bool{true, false} { + t.Run(fmt.Sprintf("hidden=%v", hidden), func(t *testing.T) { + t.Run("create branch and diff refs success", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) + testutil.Must(t, err) + testutil.Must(t, deps.catalog.CreateEntry(ctx, repo, "main", catalog.DBEntry{Path: "a/b"})) + _, err = deps.catalog.Commit(ctx, repo, "main", "first commit", "test", nil, nil, nil, false) + testutil.Must(t, err) - const newBranchName = "main2" - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: newBranchName, - Source: "main", - }) - verifyResponseOK(t, resp, err) - reference := string(resp.Body) - if len(reference) == 0 { - t.Fatalf("branch %s creation got no reference", newBranchName) - } - const objPath = "some/path" - const content = "hello world!" + const newBranchName = "main2" + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: newBranchName, + Source: "main", + Hidden: swag.Bool(hidden), + }) + verifyResponseOK(t, resp, err) + reference := string(resp.Body) + if len(reference) == 0 { + t.Fatalf("branch %s creation got no reference", newBranchName) + } + const objPath = "some/path" + const content = "hello world!" - uploadResp, err := uploadObjectHelper(t, ctx, clt, objPath, strings.NewReader(content), repo, newBranchName) - verifyResponseOK(t, uploadResp, err) + uploadResp, err := uploadObjectHelper(t, ctx, clt, objPath, strings.NewReader(content), repo, newBranchName) + verifyResponseOK(t, uploadResp, err) - if _, err := deps.catalog.Commit(ctx, repo, "main2", "commit 1", "some_user", nil, nil, nil, false); err != nil { - t.Fatalf("failed to commit 'repo1': %s", err) - } - resp2, err := clt.DiffRefsWithResponse(ctx, repo, "main", newBranchName, &apigen.DiffRefsParams{}) - verifyResponseOK(t, resp2, err) - results := resp2.JSON200.Results - if len(results) != 1 { - t.Fatalf("unexpected length of results: %d", len(results)) - } - if results[0].Path != objPath { - t.Fatalf("wrong result: %s", results[0].Path) - } - }) + if _, err := deps.catalog.Commit(ctx, repo, "main2", "commit 1", "some_user", nil, nil, nil, false); err != nil { + t.Fatalf("failed to commit 'repo1': %s", err) + } + resp2, err := clt.DiffRefsWithResponse(ctx, repo, "main", newBranchName, &apigen.DiffRefsParams{}) + verifyResponseOK(t, resp2, err) + results := resp2.JSON200.Results + if len(results) != 1 { + t.Fatalf("unexpected length of results: %d", len(results)) + } + if results[0].Path != objPath { + t.Fatalf("wrong result: %s", results[0].Path) + } + }) - t.Run("create branch missing commit", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) - testutil.Must(t, err) - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: "main3", - Source: "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", - }) - if err != nil { - t.Fatal("CreateBranch failed with error:", err) - } - if resp.JSON404 == nil { - t.Fatal("CreateBranch expected to fail with not found") - } - }) + t.Run("create branch missing commit", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) + testutil.Must(t, err) + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: "main3", + Source: "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", + Hidden: swag.Bool(hidden), + }) + if err != nil { + t.Fatal("CreateBranch failed with error:", err) + } + if resp.JSON404 == nil { + t.Fatal("CreateBranch expected to fail with not found") + } + }) - t.Run("create branch missing repo", func(t *testing.T) { - repo := testUniqueRepoName() - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: "main8", - Source: "main", - }) - if err != nil { - t.Fatal("CreateBranch failed with error:", err) - } - if resp.JSON404 == nil { - t.Fatal("CreateBranch expected not found") - } - }) + t.Run("create branch missing repo", func(t *testing.T) { + repo := testUniqueRepoName() + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: "main8", + Source: "main", + Hidden: swag.Bool(hidden), + }) + if err != nil { + t.Fatal("CreateBranch failed with error:", err) + } + if resp.JSON404 == nil { + t.Fatal("CreateBranch expected not found") + } + }) - t.Run("create branch conflict with branch", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) - testutil.Must(t, err) + t.Run("create branch conflict with branch", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) + testutil.Must(t, err) - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: "main", - Source: "main", - }) - if err != nil { - t.Fatal("CreateBranch failed with error:", err) - } - if resp.JSON409 == nil { - t.Fatal("CreateBranch expected conflict") - } - }) + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: "main", + Source: "main", + Hidden: swag.Bool(hidden), + }) + if err != nil { + t.Fatal("CreateBranch failed with error:", err) + } + if resp.JSON409 == nil { + t.Fatal("CreateBranch expected conflict") + } + }) - t.Run("create branch conflict with tag", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) - testutil.Must(t, err) + t.Run("create branch conflict with tag", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) + testutil.Must(t, err) - name := "tag123" - _, err = deps.catalog.CreateTag(ctx, repo, name, "main") - testutil.Must(t, err) + name := "tag123" + _, err = deps.catalog.CreateTag(ctx, repo, name, "main") + testutil.Must(t, err) - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: name, - Source: "main", - }) - if err != nil { - t.Fatal("CreateBranch failed with error:", err) - } - if resp.JSON409 == nil { - t.Fatal("CreateBranch expected conflict") - } - }) + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: name, + Source: "main", + Hidden: swag.Bool(hidden), + }) + if err != nil { + t.Fatal("CreateBranch failed with error:", err) + } + if resp.JSON409 == nil { + t.Fatal("CreateBranch expected conflict") + } + }) - t.Run("create branch conflict with commit", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) - testutil.Must(t, err) + t.Run("create branch conflict with commit", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", false) + testutil.Must(t, err) - log, err := deps.catalog.GetCommit(ctx, repo, "main") - testutil.Must(t, err) + log, err := deps.catalog.GetCommit(ctx, repo, "main") + testutil.Must(t, err) - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: log.Reference, - Source: "main", - }) - if err != nil { - t.Fatal("CreateBranch failed with error:", err) - } - if resp.JSON409 == nil { - t.Fatal("CreateBranch expected conflict, got", resp.Status()) - } - }) + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: log.Reference, + Source: "main", + Hidden: swag.Bool(hidden), + }) + if err != nil { + t.Fatal("CreateBranch failed with error:", err) + } + if resp.JSON409 == nil { + t.Fatal("CreateBranch expected conflict, got", resp.Status()) + } + }) - t.Run("read-only repository", func(t *testing.T) { - repo := testUniqueRepoName() - _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", true) - testutil.Must(t, err) - testutil.Must(t, deps.catalog.CreateEntry(ctx, repo, "main", catalog.DBEntry{Path: "a/b"}, graveler.WithForce(true))) - _, err = deps.catalog.Commit(ctx, repo, "main", "first commit", "test", nil, nil, nil, false, graveler.WithForce(true)) - testutil.Must(t, err) + t.Run("read-only repository", func(t *testing.T) { + repo := testUniqueRepoName() + _, err := deps.catalog.CreateRepository(ctx, repo, onBlock(deps, "foo1"), "main", true) + testutil.Must(t, err) + testutil.Must(t, deps.catalog.CreateEntry(ctx, repo, "main", catalog.DBEntry{Path: "a/b"}, graveler.WithForce(true))) + _, err = deps.catalog.Commit(ctx, repo, "main", "first commit", "test", nil, nil, nil, false, graveler.WithForce(true)) + testutil.Must(t, err) - const newBranchName = "main2" - resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: newBranchName, - Source: "main", - }) - testutil.Must(t, err) - if resp.StatusCode() != http.StatusForbidden { - t.Fatal("CreateBranch expected 403 forbidden, got", resp.Status()) - } - resp, err = clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ - Name: newBranchName, - Source: "main", - Force: swag.Bool(true), + const newBranchName = "main2" + resp, err := clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: newBranchName, + Source: "main", + Hidden: swag.Bool(hidden), + }) + testutil.Must(t, err) + if resp.StatusCode() != http.StatusForbidden { + t.Fatal("CreateBranch expected 403 forbidden, got", resp.Status()) + } + resp, err = clt.CreateBranchWithResponse(ctx, repo, apigen.CreateBranchJSONRequestBody{ + Name: newBranchName, + Source: "main", + Force: swag.Bool(true), + Hidden: swag.Bool(hidden), + }) + verifyResponseOK(t, resp, err) + }) }) - verifyResponseOK(t, resp, err) - }) + } } func TestController_DiffRefsHandler(t *testing.T) { diff --git a/pkg/catalog/catalog.go b/pkg/catalog/catalog.go index b0b6155db5b..99cb8d14ed5 100644 --- a/pkg/catalog/catalog.go +++ b/pkg/catalog/catalog.go @@ -729,7 +729,7 @@ func (c *Catalog) DeleteBranch(ctx context.Context, repositoryID string, branch return c.Store.DeleteBranch(ctx, repository, branchID, opts...) } -func (c *Catalog) ListBranches(ctx context.Context, repositoryID string, prefix string, limit int, after string) ([]*Branch, bool, error) { +func (c *Catalog) ListBranches(ctx context.Context, repositoryID string, prefix string, limit int, after string, opts ...graveler.ListOptionsFunc) ([]*Branch, bool, error) { if err := validator.Validate([]validator.ValidateArg{ {Name: "repository", Value: repositoryID, Fn: graveler.ValidateRepositoryID}, }); err != nil { @@ -744,7 +744,7 @@ func (c *Catalog) ListBranches(ctx context.Context, repositoryID string, prefix if limit < 0 || limit > ListBranchesLimitMax { limit = ListBranchesLimitMax } - it, err := c.Store.ListBranches(ctx, repository) + it, err := c.Store.ListBranches(ctx, repository, opts...) if err != nil { return nil, false, err } diff --git a/pkg/catalog/fake_graveler_test.go b/pkg/catalog/fake_graveler_test.go index 75569bc260e..869f25fdaf6 100644 --- a/pkg/catalog/fake_graveler_test.go +++ b/pkg/catalog/fake_graveler_test.go @@ -214,7 +214,7 @@ func (g *FakeGraveler) Log(ctx context.Context, repository *graveler.RepositoryR panic("implement me") } -func (g *FakeGraveler) ListBranches(_ context.Context, _ *graveler.RepositoryRecord) (graveler.BranchIterator, error) { +func (g *FakeGraveler) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord, opts ...graveler.ListOptionsFunc) (graveler.BranchIterator, error) { if g.Err != nil { return nil, g.Err } diff --git a/pkg/catalog/gc_write_uncommitted.go b/pkg/catalog/gc_write_uncommitted.go index 099f41451b1..938e1cc0f1d 100644 --- a/pkg/catalog/gc_write_uncommitted.go +++ b/pkg/catalog/gc_write_uncommitted.go @@ -17,7 +17,7 @@ func gcWriteUncommitted(ctx context.Context, store Store, repository *graveler.R } pw.CompressionType = parquet.CompressionCodec_GZIP - branchIterator, err := store.ListBranches(ctx, repository) + branchIterator, err := store.ListBranches(ctx, repository, graveler.WithShowHidden(true)) if err != nil { return nil, false, err } diff --git a/pkg/graveler/graveler.go b/pkg/graveler/graveler.go index 3edeacb68f8..fdf25e8bea7 100644 --- a/pkg/graveler/graveler.go +++ b/pkg/graveler/graveler.go @@ -194,6 +194,8 @@ type SetOptions struct { Force bool // AllowEmpty set to true will allow committing an empty commit. AllowEmpty bool + // Hidden Will create the branch with the hidden property + Hidden bool } type SetOptionsFunc func(opts *SetOptions) @@ -224,6 +226,34 @@ func WithAllowEmpty(v bool) SetOptionsFunc { } } +func WithHidden(v bool) SetOptionsFunc { + return func(opts *SetOptions) { + opts.Hidden = v + } +} + +// ListOptions controls list request defaults +type ListOptions struct { + // Shows entities marked as hidden + ShowHidden bool +} + +type ListOptionsFunc func(opts *ListOptions) + +func NewListOptions(opts []ListOptionsFunc) *ListOptions { + options := &ListOptions{} + for _, opt := range opts { + opt(options) + } + return options +} + +func WithShowHidden(v bool) ListOptionsFunc { + return func(opts *ListOptions) { + opts.ShowHidden = v + } +} + // function/methods receiving the following basic types could assume they passed validation // StorageNamespace is the URI to the storage location @@ -456,6 +486,7 @@ type Branch struct { SealedTokens []StagingToken // CompactedBaseMetaRangeID - the MetaRangeID of the last compaction's CompactedBaseMetaRangeID MetaRangeID + Hidden bool } // BranchRecord holds BranchID with the associated Branch data @@ -602,7 +633,7 @@ type VersionController interface { Log(ctx context.Context, repository *RepositoryRecord, commitID CommitID, firstParent bool, since *time.Time) (CommitIterator, error) // ListBranches lists branches on repositories - ListBranches(ctx context.Context, repository *RepositoryRecord) (BranchIterator, error) + ListBranches(ctx context.Context, repository *RepositoryRecord, opts ...ListOptionsFunc) (BranchIterator, error) // DeleteBranch deletes branch from repository DeleteBranch(ctx context.Context, repository *RepositoryRecord, branchID BranchID, opts ...SetOptionsFunc) error @@ -896,7 +927,7 @@ type RefManager interface { DeleteBranch(ctx context.Context, repository *RepositoryRecord, branchID BranchID) error // ListBranches lists branches - ListBranches(ctx context.Context, repository *RepositoryRecord) (BranchIterator, error) + ListBranches(ctx context.Context, repository *RepositoryRecord, opts ListOptions) (BranchIterator, error) // GCBranchIterator TODO (niro): Remove when DB implementation is deleted // GCBranchIterator temporary WA to support both DB and KV GC BranchIterator, which iterates over branches by order of commit ID @@ -1251,6 +1282,7 @@ func (g *Graveler) CreateBranch(ctx context.Context, repository *RepositoryRecor CommitID: reference.CommitID, StagingToken: GenerateStagingToken(repository.RepositoryID, branchID), SealedTokens: make([]StagingToken, 0), + Hidden: options.Hidden, } storageNamespace := repository.StorageNamespace var preRunID string @@ -1527,8 +1559,9 @@ func (g *Graveler) Log(ctx context.Context, repository *RepositoryRecord, commit return g.RefManager.Log(ctx, repository, commitID, firstParent, since) } -func (g *Graveler) ListBranches(ctx context.Context, repository *RepositoryRecord) (BranchIterator, error) { - return g.RefManager.ListBranches(ctx, repository) +func (g *Graveler) ListBranches(ctx context.Context, repository *RepositoryRecord, opts ...ListOptionsFunc) (BranchIterator, error) { + options := NewListOptions(opts) + return g.RefManager.ListBranches(ctx, repository, *options) } func (g *Graveler) DeleteBranch(ctx context.Context, repository *RepositoryRecord, branchID BranchID, opts ...SetOptionsFunc) error { @@ -3392,7 +3425,7 @@ func (g *Graveler) DumpCommits(ctx context.Context, repository *RepositoryRecord } func (g *Graveler) DumpBranches(ctx context.Context, repository *RepositoryRecord) (*MetaRangeID, error) { - iter, err := g.RefManager.ListBranches(ctx, repository) + iter, err := g.RefManager.ListBranches(ctx, repository, ListOptions{ShowHidden: true}) if err != nil { return nil, err } diff --git a/pkg/graveler/graveler.pb.go b/pkg/graveler/graveler.pb.go index 2009743f5de..0ac0bf7bc63 100644 --- a/pkg/graveler/graveler.pb.go +++ b/pkg/graveler/graveler.pb.go @@ -266,6 +266,7 @@ type BranchData struct { CommitId string `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id,omitempty"` StagingToken string `protobuf:"bytes,3,opt,name=staging_token,json=stagingToken,proto3" json:"staging_token,omitempty"` SealedTokens []string `protobuf:"bytes,4,rep,name=sealed_tokens,json=sealedTokens,proto3" json:"sealed_tokens,omitempty"` + Hidden bool `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"` } func (x *BranchData) Reset() { @@ -328,6 +329,13 @@ func (x *BranchData) GetSealedTokens() []string { return nil } +func (x *BranchData) GetHidden() bool { + if x != nil { + return x.Hidden + } + return false +} + type TagData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1046,7 +1054,7 @@ var file_graveler_graveler_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x55, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x0a, 0x42, + 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x0a, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, @@ -1055,160 +1063,161 @@ var file_graveler_graveler_proto_rawDesc = []byte{ 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, - 0x22, 0x36, 0x0a, 0x07, 0x54, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x64, 0x22, 0x9e, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, - 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, - 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, - 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, - 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3b, 0x0a, 0x0d, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9a, 0x02, 0x0a, 0x16, 0x47, 0x61, - 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x75, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x79, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x74, - 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x79, 0x73, 0x12, 0x81, 0x01, 0x0a, 0x15, 0x62, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x64, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4d, 0x2e, 0x69, 0x6f, 0x2e, - 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, - 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x62, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x79, 0x73, 0x1a, 0x46, - 0x0a, 0x18, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, - 0x6e, 0x44, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x73, 0x0a, 0x1e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, - 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x51, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x3b, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, - 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, - 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, - 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xcb, 0x02, 0x0a, 0x15, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0xa0, 0x01, 0x0a, 0x21, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x5f, 0x74, 0x6f, 0x5f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x56, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, - 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, - 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x61, 0x74, - 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1d, 0x62, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, - 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x8e, 0x01, 0x0a, 0x22, 0x42, 0x72, 0x61, - 0x6e, 0x63, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x52, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3c, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, + 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x22, 0x36, 0x0a, 0x07, 0x54, 0x61, 0x67, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x64, + 0x22, 0x9e, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, + 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, + 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, + 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x9a, 0x02, 0x0a, 0x16, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x64, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, + 0x79, 0x73, 0x12, 0x81, 0x01, 0x0a, 0x15, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x72, 0x65, + 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x4d, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, + 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, + 0x72, 0x2e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x13, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, + 0x6f, 0x6e, 0x44, 0x61, 0x79, 0x73, 0x1a, 0x46, 0x0a, 0x18, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x73, + 0x0a, 0x1e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x51, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, + 0x3b, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, + 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x42, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xcb, 0x02, 0x0a, 0x15, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0xa0, 0x01, + 0x0a, 0x21, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x5f, 0x74, 0x6f, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x69, 0x6f, 0x2e, 0x74, + 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, + 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x42, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x1d, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x54, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x1a, 0x8e, 0x01, 0x0a, 0x22, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x6e, 0x54, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, + 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, + 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x53, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2b, 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x22, 0x92, 0x02, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x64, + 0x12, 0x40, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x53, 0x0a, 0x0f, 0x53, 0x74, 0x61, - 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, - 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2b, - 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x92, 0x02, 0x0a, 0x10, - 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xa1, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x70, + 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x69, 0x6f, + 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, + 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, + 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc5, 0x03, 0x0a, + 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x39, - 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x72, 0x61, 0x6e, - 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, - 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, - 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, - 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0xa1, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x54, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, - 0x65, 0x72, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc5, 0x03, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, - 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, - 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, - 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x12, 0x20, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x48, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, - 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x0c, - 0x0a, 0x0a, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x2a, 0x2e, 0x0a, 0x0f, - 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, - 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x3e, 0x0a, 0x1d, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x11, 0x0a, - 0x0d, 0x53, 0x54, 0x41, 0x47, 0x49, 0x4e, 0x47, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x2a, 0x35, 0x0a, 0x11, + 0x12, 0x47, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2f, 0x2e, 0x69, 0x6f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, + 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2e, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, - 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x52, 0x47, 0x45, - 0x44, 0x10, 0x02, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2f, 0x6c, 0x61, 0x6b, 0x65, - 0x66, 0x73, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x2a, 0x2e, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x3e, 0x0a, 0x1d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x47, 0x49, 0x4e, 0x47, + 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, + 0x49, 0x54, 0x10, 0x01, 0x2a, 0x35, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, + 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x01, 0x12, + 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x52, 0x47, 0x45, 0x44, 0x10, 0x02, 0x42, 0x26, 0x5a, 0x24, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x2f, 0x6c, 0x61, 0x6b, 0x65, 0x66, 0x73, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x65, + 0x6c, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/graveler/graveler.proto b/pkg/graveler/graveler.proto index d81ec8224e1..7f48453bf50 100644 --- a/pkg/graveler/graveler.proto +++ b/pkg/graveler/graveler.proto @@ -25,6 +25,7 @@ message BranchData { string commit_id = 2; string staging_token = 3; repeated string sealed_tokens = 4; + bool hidden = 5; } message TagData { diff --git a/pkg/graveler/mock/graveler.go b/pkg/graveler/mock/graveler.go index dbd2c30d1d3..2998afe71a7 100644 --- a/pkg/graveler/mock/graveler.go +++ b/pkg/graveler/mock/graveler.go @@ -667,18 +667,23 @@ func (mr *MockVersionControllerMockRecorder) Import(ctx, repository, destination } // ListBranches mocks base method. -func (m *MockVersionController) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { +func (m *MockVersionController) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord, opts ...graveler.ListOptionsFunc) (graveler.BranchIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListBranches", ctx, repository) + varargs := []interface{}{ctx, repository} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListBranches", varargs...) ret0, _ := ret[0].(graveler.BranchIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // ListBranches indicates an expected call of ListBranches. -func (mr *MockVersionControllerMockRecorder) ListBranches(ctx, repository interface{}) *gomock.Call { +func (mr *MockVersionControllerMockRecorder) ListBranches(ctx, repository interface{}, opts ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockVersionController)(nil).ListBranches), ctx, repository) + varargs := append([]interface{}{ctx, repository}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockVersionController)(nil).ListBranches), varargs...) } // ListRepositories mocks base method. @@ -2398,18 +2403,18 @@ func (mr *MockRefManagerMockRecorder) GetTag(ctx, repository, tagID interface{}) } // ListBranches mocks base method. -func (m *MockRefManager) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { +func (m *MockRefManager) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord, opts graveler.ListOptions) (graveler.BranchIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListBranches", ctx, repository) + ret := m.ctrl.Call(m, "ListBranches", ctx, repository, opts) ret0, _ := ret[0].(graveler.BranchIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // ListBranches indicates an expected call of ListBranches. -func (mr *MockRefManagerMockRecorder) ListBranches(ctx, repository interface{}) *gomock.Call { +func (mr *MockRefManagerMockRecorder) ListBranches(ctx, repository, opts interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockRefManager)(nil).ListBranches), ctx, repository) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockRefManager)(nil).ListBranches), ctx, repository, opts) } // ListCommits mocks base method. diff --git a/pkg/graveler/ref/branch_iterator.go b/pkg/graveler/ref/branch_iterator.go index 1e9f975df52..2e2ee3db828 100644 --- a/pkg/graveler/ref/branch_iterator.go +++ b/pkg/graveler/ref/branch_iterator.go @@ -23,9 +23,10 @@ type BranchSimpleIterator struct { repoPartition string value *graveler.BranchRecord err error + showHidden bool } -func NewBranchSimpleIterator(ctx context.Context, store kv.Store, repo *graveler.RepositoryRecord) (*BranchSimpleIterator, error) { +func NewBranchSimpleIterator(ctx context.Context, store kv.Store, repo *graveler.RepositoryRecord, opts graveler.ListOptions) (*BranchSimpleIterator, error) { repoPartition := graveler.RepoPartition(repo) it, err := kv.NewPrimaryIterator(ctx, store, (&graveler.BranchData{}).ProtoReflect().Type(), repoPartition, []byte(graveler.BranchPath("")), kv.IteratorOptionsFrom([]byte(""))) @@ -40,6 +41,7 @@ func NewBranchSimpleIterator(ctx context.Context, store kv.Store, repo *graveler repoPartition: repoPartition, value: nil, err: nil, + showHidden: opts.ShowHidden, }, nil } @@ -47,26 +49,30 @@ func (bi *BranchSimpleIterator) Next() bool { if bi.Err() != nil { return false } - if !bi.itr.Next() { - bi.value = nil - return false - } - entry := bi.itr.Entry() - if entry == nil { - bi.err = graveler.ErrInvalid - return false - } - value, ok := entry.Value.(*graveler.BranchData) - if !ok { - bi.err = graveler.ErrReadingFromStore - return false - } - - bi.value = &graveler.BranchRecord{ - BranchID: graveler.BranchID(value.Id), - Branch: branchFromProto(value), + for { + if !bi.itr.Next() { + bi.value = nil + return false + } + entry := bi.itr.Entry() + if entry == nil { + bi.err = graveler.ErrInvalid + return false + } + value, ok := entry.Value.(*graveler.BranchData) + if !ok { + bi.err = graveler.ErrReadingFromStore + return false + } + if value.Hidden && !bi.showHidden { // Skip hidden branches if showHidden is false + continue + } + bi.value = &graveler.BranchRecord{ + BranchID: graveler.BranchID(value.Id), + Branch: branchFromProto(value), + } + return true } - return true } func (bi *BranchSimpleIterator) SeekGE(id graveler.BranchID) { @@ -111,12 +117,12 @@ func (b *BranchByCommitIterator) SortByCommitID(i, j int) bool { return b.values[i].CommitID.String() <= b.values[j].CommitID.String() } -func NewBranchByCommitIterator(ctx context.Context, store kv.Store, repo *graveler.RepositoryRecord) (*BranchByCommitIterator, error) { +func NewBranchByCommitIterator(ctx context.Context, store kv.Store, repo *graveler.RepositoryRecord, opts graveler.ListOptions) (*BranchByCommitIterator, error) { bi := &BranchByCommitIterator{ ctx: ctx, values: make([]*graveler.BranchRecord, 0), } - itr, err := NewBranchSimpleIterator(ctx, store, repo) + itr, err := NewBranchSimpleIterator(ctx, store, repo, opts) if err != nil { return nil, err } diff --git a/pkg/graveler/ref/branch_iterator_test.go b/pkg/graveler/ref/branch_iterator_test.go index 25ccb98c31d..34ec1a70e71 100644 --- a/pkg/graveler/ref/branch_iterator_test.go +++ b/pkg/graveler/ref/branch_iterator_test.go @@ -33,7 +33,7 @@ func TestBranchSimpleIterator(t *testing.T) { } t.Run("listing all branches", func(t *testing.T) { - iter, err := ref.NewBranchSimpleIterator(ctx, kvStore, repo) + iter, err := ref.NewBranchSimpleIterator(ctx, kvStore, repo, graveler.ListOptions{ShowHidden: true}) require.NoError(t, err) ids := make([]graveler.BranchID, 0) for iter.Next() { @@ -51,7 +51,7 @@ func TestBranchSimpleIterator(t *testing.T) { }) t.Run("listing branches SeekGE", func(t *testing.T) { - iter, err := ref.NewBranchSimpleIterator(ctx, kvStore, repo) + iter, err := ref.NewBranchSimpleIterator(ctx, kvStore, repo, graveler.ListOptions{ShowHidden: true}) require.NoError(t, err) iter.SeekGE("b") ids := make([]graveler.BranchID, 0) @@ -110,7 +110,7 @@ func TestBranchByCommitIterator(t *testing.T) { } t.Run("listing all branches", func(t *testing.T) { - iter, err := ref.NewBranchByCommitIterator(ctx, kvStore, repo) + iter, err := ref.NewBranchByCommitIterator(ctx, kvStore, repo, graveler.ListOptions{ShowHidden: true}) require.NoError(t, err) ids := []graveler.CommitID{"mainCommitNotFound"} for iter.Next() { diff --git a/pkg/graveler/ref/manager.go b/pkg/graveler/ref/manager.go index d8889c53da4..35ec46a9d43 100644 --- a/pkg/graveler/ref/manager.go +++ b/pkg/graveler/ref/manager.go @@ -51,6 +51,7 @@ func branchFromProto(pb *graveler.BranchData) *graveler.Branch { CommitID: graveler.CommitID(pb.CommitId), StagingToken: graveler.StagingToken(pb.StagingToken), SealedTokens: sealedTokens, + Hidden: pb.Hidden, } return branch } @@ -65,6 +66,7 @@ func protoFromBranch(branchID graveler.BranchID, b *graveler.Branch) *graveler.B CommitId: b.CommitID.String(), StagingToken: b.StagingToken.String(), SealedTokens: sealedTokens, + Hidden: b.Hidden, } return branch } @@ -239,7 +241,7 @@ func (m *Manager) updateRepoState(ctx context.Context, repo *graveler.Repository } func (m *Manager) deleteRepositoryBranches(ctx context.Context, repository *graveler.RepositoryRecord) error { - itr, err := m.ListBranches(ctx, repository) + itr, err := m.ListBranches(ctx, repository, graveler.ListOptions{ShowHidden: true}) if err != nil { return err } @@ -475,12 +477,12 @@ func (m *Manager) DeleteBranch(ctx context.Context, repository *graveler.Reposit return m.kvStore.Delete(ctx, []byte(graveler.RepoPartition(repository)), []byte(graveler.BranchPath(branchID))) } -func (m *Manager) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { - return NewBranchSimpleIterator(ctx, m.kvStore, repository) +func (m *Manager) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord, opts graveler.ListOptions) (graveler.BranchIterator, error) { + return NewBranchSimpleIterator(ctx, m.kvStore, repository, opts) } func (m *Manager) GCBranchIterator(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { - return NewBranchByCommitIterator(ctx, m.kvStore, repository) + return NewBranchByCommitIterator(ctx, m.kvStore, repository, graveler.ListOptions{ShowHidden: true}) } func (m *Manager) GetTag(ctx context.Context, repository *graveler.RepositoryRecord, tagID graveler.TagID) (*graveler.CommitID, error) { diff --git a/pkg/graveler/ref/manager_test.go b/pkg/graveler/ref/manager_test.go index bf3cb35314c..1c5c7af2857 100644 --- a/pkg/graveler/ref/manager_test.go +++ b/pkg/graveler/ref/manager_test.go @@ -339,38 +339,57 @@ func TestManager_CreateBranch(t *testing.T) { }) testutil.Must(t, err) - err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c1", StagingToken: "s1"}) - testutil.MustDo(t, "create branch f1", err) - - br, err := r.GetBranch(ctx, repository, "f1") - testutil.MustDo(t, "get f1 branch", err) - if br == nil { - t.Fatal("get branch got nil") - } - if br.CommitID != "c1" { - t.Fatalf("unexpected commit for branch f1: %s - expected: c1", br.CommitID) + testCases := []struct { + Name string + Hidden bool + }{ + { + Name: "not hidden", + Hidden: false, + }, + { + Name: "hidden", + Hidden: true, + }, } + for _, tt := range testCases { + t.Run(tt.Name, func(t *testing.T) { + branchName := graveler.BranchID(tt.Name) + err = r.CreateBranch(ctx, repository, branchName, graveler.Branch{CommitID: "c1", StagingToken: "s1", Hidden: tt.Hidden}) + testutil.MustDo(t, "create branch", err) - // check we can't create existing - err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c2", StagingToken: "s2"}) - if !errors.Is(err, graveler.ErrBranchExists) { - t.Fatalf("CreateBranch() err = %s, expected already exists", err) - } - // overwrite by delete and create - err = r.DeleteBranch(ctx, repository, "f1") - testutil.MustDo(t, "delete branch f1", err) + br, err := r.GetBranch(ctx, repository, branchName) + testutil.MustDo(t, "get branch", err) + if br == nil { + t.Fatal("get branch got nil") + } + if br.CommitID != "c1" { + t.Fatalf("unexpected commit for branch: %s - expected: c1", br.CommitID) + } + require.Equal(t, tt.Hidden, br.Hidden) - err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c2", StagingToken: "s2"}) - testutil.MustDo(t, "create branch f1", err) + // check we can't create existing + err = r.CreateBranch(ctx, repository, branchName, graveler.Branch{CommitID: "c2", StagingToken: "s2", Hidden: tt.Hidden}) + if !errors.Is(err, graveler.ErrBranchExists) { + t.Fatalf("CreateBranch() err = %s, expected already exists", err) + } + // overwrite by delete and create + err = r.DeleteBranch(ctx, repository, branchName) + testutil.MustDo(t, "delete branch", err) - br, err = r.GetBranch(ctx, repository, "f1") - testutil.MustDo(t, "get f1 branch", err) + err = r.CreateBranch(ctx, repository, branchName, graveler.Branch{CommitID: "c2", StagingToken: "s2", Hidden: tt.Hidden}) + testutil.MustDo(t, "create branch", err) - if br == nil { - t.Fatal("get branch got nil") - } - if br.CommitID != "c2" { - t.Fatalf("unexpected commit for branch f1: %s - expected: c2", br.CommitID) + br, err = r.GetBranch(ctx, repository, branchName) + testutil.MustDo(t, "get f1 branch", err) + + if br == nil { + t.Fatal("get branch got nil") + } + if br.CommitID != "c2" { + t.Fatalf("unexpected commit for branch: %s - expected: c2", br.CommitID) + } + }) } } @@ -512,13 +531,46 @@ func TestManager_ListBranches(t *testing.T) { }) testutil.Must(t, err) - for _, b := range []graveler.BranchID{"a", "aa", "c", "b", "z", "f"} { + visibleBranches := []graveler.BranchID{"a", "ab", "ca", "ba", "za", "fa"} + hiddenBranches := []graveler.BranchID{"aa", "ac", "cb", "bb", "zb", "fb"} + allBranches := append(visibleBranches, hiddenBranches...) + for _, b := range visibleBranches { testutil.Must(t, r.SetBranch(context.Background(), repository, b, graveler.Branch{ CommitID: "c2", })) } + for _, b := range hiddenBranches { + testutil.Must(t, r.SetBranch(context.Background(), repository, b, graveler.Branch{ + CommitID: "c2", + Hidden: true, + })) + } + + // List only visible branches + iter, err := r.ListBranches(context.Background(), repository, graveler.ListOptions{ShowHidden: false}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer iter.Close() + + var bvs []graveler.BranchID + for iter.Next() { + b := iter.Value() + bvs = append(bvs, b.BranchID) + } + if iter.Err() != nil { + t.Fatalf("unexpected error: %v", iter.Err()) + } + visibleBranches = append(visibleBranches, "main") + sort.Slice(visibleBranches, func(i, j int) bool { + return visibleBranches[i] < visibleBranches[j] + }) + if !reflect.DeepEqual(bvs, visibleBranches) { + t.Fatalf("unexpected branch list: %v", bvs) + } - iter, err := r.ListBranches(context.Background(), repository) + // List all branches + iter, err = r.ListBranches(context.Background(), repository, graveler.ListOptions{ShowHidden: true}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -532,7 +584,11 @@ func TestManager_ListBranches(t *testing.T) { if iter.Err() != nil { t.Fatalf("unexpected error: %v", iter.Err()) } - if !reflect.DeepEqual(bs, []graveler.BranchID{"a", "aa", "b", "c", "f", "main", "z"}) { + allBranches = append(allBranches, "main") + sort.Slice(allBranches, func(i, j int) bool { + return allBranches[i] < allBranches[j] + }) + if !reflect.DeepEqual(bs, allBranches) { t.Fatalf("unexpected branch list: %v", bs) } } diff --git a/pkg/graveler/testutil/fakes.go b/pkg/graveler/testutil/fakes.go index 51f0771a60e..468ff6ed8ba 100644 --- a/pkg/graveler/testutil/fakes.go +++ b/pkg/graveler/testutil/fakes.go @@ -366,7 +366,7 @@ func (m *RefsFake) DeleteBranch(context.Context, *graveler.RepositoryRecord, gra return nil } -func (m *RefsFake) ListBranches(context.Context, *graveler.RepositoryRecord) (graveler.BranchIterator, error) { +func (m *RefsFake) ListBranches(context.Context, *graveler.RepositoryRecord, graveler.ListOptions) (graveler.BranchIterator, error) { return m.ListBranchesRes, nil } From a6a898015b5e65c27442ca56b13fa961ddfe1c63 Mon Sep 17 00:00:00 2001 From: N-o-Z Date: Mon, 18 Nov 2024 12:37:35 -0500 Subject: [PATCH 2/5] Update api/swagger.yml Co-authored-by: Ariel Shaqed (Scolnicov) --- api/swagger.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/swagger.yml b/api/swagger.yml index 3c7810590a5..f96389d2f1f 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -695,7 +695,7 @@ components: default: false hidden: type: boolean - description: When set, branch will not show up when listing branches by default + description: *EXPERIMENTAL* When set, branch will not show up when listing branches by default. default: false TagCreation: From c5b5ad3a4fff05eb9027cceee33fe768babece28 Mon Sep 17 00:00:00 2001 From: Nir Ozery Date: Wed, 20 Nov 2024 17:32:52 -0500 Subject: [PATCH 3/5] CR Fixes --- pkg/catalog/catalog_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/catalog/catalog_test.go b/pkg/catalog/catalog_test.go index fa095176ddc..6023cad2352 100644 --- a/pkg/catalog/catalog_test.go +++ b/pkg/catalog/catalog_test.go @@ -759,7 +759,7 @@ func createPrepareUncommittedTestScenario(t *testing.T, repositoryID string, num test.RefManager.EXPECT().GetRepository(gomock.Any(), graveler.RepositoryID(repositoryID)).MinTimes(1).Return(repository, nil) // expect tracked addresses does not list branches, so remove one and keep at least the first - test.RefManager.EXPECT().ListBranches(gomock.Any(), gomock.Any()).MinTimes(1).Return(gUtils.NewFakeBranchIterator(branches), nil) + test.RefManager.EXPECT().ListBranches(gomock.Any(), gomock.Any(), gomock.Any()).MinTimes(1).Return(gUtils.NewFakeBranchIterator(branches), nil) for i := 0; i < len(branches); i++ { sort.Slice(records[i], func(ii, jj int) bool { return bytes.Compare(records[i][ii].Key, records[i][jj].Key) < 0 From bdcf023a83cf2ef53c787ecc65d7abf17e243f84 Mon Sep 17 00:00:00 2001 From: Nir Ozery Date: Thu, 21 Nov 2024 22:44:37 -0500 Subject: [PATCH 4/5] CR Fixes 2 --- api/swagger.yml | 2 +- clients/java-legacy/api/openapi.yaml | 2 +- clients/java-legacy/docs/BranchCreation.md | 2 +- .../io/lakefs/clients/api/model/BranchCreation.java | 4 ++-- clients/java/api/openapi.yaml | 2 +- clients/java/docs/BranchCreation.md | 2 +- .../io/lakefs/clients/sdk/model/BranchCreation.java | 2 +- clients/python-legacy/docs/BranchCreation.md | 2 +- .../lakefs_client/model/branch_creation.py | 4 ++-- clients/python/docs/BranchCreation.md | 2 +- clients/python/lakefs_sdk/models/branch_creation.py | 2 +- clients/rust/docs/BranchCreation.md | 2 +- clients/rust/src/models/branch_creation.rs | 2 +- docs/assets/js/swagger.yml | 2 +- pkg/api/controller_test.go | 10 ++++++---- 15 files changed, 22 insertions(+), 20 deletions(-) diff --git a/api/swagger.yml b/api/swagger.yml index f96389d2f1f..37f55b2ae8f 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -695,7 +695,7 @@ components: default: false hidden: type: boolean - description: *EXPERIMENTAL* When set, branch will not show up when listing branches by default. + description: When set, branch will not show up when listing branches by default. *EXPERIMENTAL* default: false TagCreation: diff --git a/clients/java-legacy/api/openapi.yaml b/clients/java-legacy/api/openapi.yaml index 308ff3d647a..9a8530fcf62 100644 --- a/clients/java-legacy/api/openapi.yaml +++ b/clients/java-legacy/api/openapi.yaml @@ -8272,7 +8272,7 @@ components: hidden: default: false description: When set, branch will not show up when listing branches by - default + default. *EXPERIMENTAL* type: boolean required: - name diff --git a/clients/java-legacy/docs/BranchCreation.md b/clients/java-legacy/docs/BranchCreation.md index df8885fd95c..9ca79a84cf8 100644 --- a/clients/java-legacy/docs/BranchCreation.md +++ b/clients/java-legacy/docs/BranchCreation.md @@ -10,7 +10,7 @@ Name | Type | Description | Notes **name** | **String** | | **source** | **String** | | **force** | **Boolean** | | [optional] -**hidden** | **Boolean** | When set, branch will not show up when listing branches by default | [optional] +**hidden** | **Boolean** | When set, branch will not show up when listing branches by default. *EXPERIMENTAL* | [optional] diff --git a/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java b/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java index bc5a6805908..8034b7cd36c 100644 --- a/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java +++ b/clients/java-legacy/src/main/java/io/lakefs/clients/api/model/BranchCreation.java @@ -122,11 +122,11 @@ public BranchCreation hidden(Boolean hidden) { } /** - * When set, branch will not show up when listing branches by default + * When set, branch will not show up when listing branches by default. *EXPERIMENTAL* * @return hidden **/ @javax.annotation.Nullable - @ApiModelProperty(value = "When set, branch will not show up when listing branches by default") + @ApiModelProperty(value = "When set, branch will not show up when listing branches by default. *EXPERIMENTAL*") public Boolean getHidden() { return hidden; diff --git a/clients/java/api/openapi.yaml b/clients/java/api/openapi.yaml index 690ac3f1692..cfe8a2a3452 100644 --- a/clients/java/api/openapi.yaml +++ b/clients/java/api/openapi.yaml @@ -8246,7 +8246,7 @@ components: hidden: default: false description: "When set, branch will not show up when listing branches by\ - \ default" + \ default. *EXPERIMENTAL*" type: boolean required: - name diff --git a/clients/java/docs/BranchCreation.md b/clients/java/docs/BranchCreation.md index 305cb2b84d1..f1b336dd9d2 100644 --- a/clients/java/docs/BranchCreation.md +++ b/clients/java/docs/BranchCreation.md @@ -10,7 +10,7 @@ |**name** | **String** | | | |**source** | **String** | | | |**force** | **Boolean** | | [optional] | -|**hidden** | **Boolean** | When set, branch will not show up when listing branches by default | [optional] | +|**hidden** | **Boolean** | When set, branch will not show up when listing branches by default. *EXPERIMENTAL* | [optional] | diff --git a/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java b/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java index 57f97626b02..954fe481d0f 100644 --- a/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java +++ b/clients/java/src/main/java/io/lakefs/clients/sdk/model/BranchCreation.java @@ -141,7 +141,7 @@ public BranchCreation hidden(Boolean hidden) { } /** - * When set, branch will not show up when listing branches by default + * When set, branch will not show up when listing branches by default. *EXPERIMENTAL* * @return hidden **/ @javax.annotation.Nullable diff --git a/clients/python-legacy/docs/BranchCreation.md b/clients/python-legacy/docs/BranchCreation.md index d5dab1c5619..72a6025d161 100644 --- a/clients/python-legacy/docs/BranchCreation.md +++ b/clients/python-legacy/docs/BranchCreation.md @@ -7,7 +7,7 @@ Name | Type | Description | Notes **name** | **str** | | **source** | **str** | | **force** | **bool** | | [optional] if omitted the server will use the default value of False -**hidden** | **bool** | When set, branch will not show up when listing branches by default | [optional] if omitted the server will use the default value of False +**hidden** | **bool** | When set, branch will not show up when listing branches by default. *EXPERIMENTAL* | [optional] if omitted the server will use the default value of False **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/clients/python-legacy/lakefs_client/model/branch_creation.py b/clients/python-legacy/lakefs_client/model/branch_creation.py index 51af3d1356f..65afc799e77 100644 --- a/clients/python-legacy/lakefs_client/model/branch_creation.py +++ b/clients/python-legacy/lakefs_client/model/branch_creation.py @@ -146,7 +146,7 @@ def _from_openapi_data(cls, name, source, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) force (bool): [optional] if omitted the server will use the default value of False # noqa: E501 - hidden (bool): When set, branch will not show up when listing branches by default. [optional] if omitted the server will use the default value of False # noqa: E501 + hidden (bool): When set, branch will not show up when listing branches by default. *EXPERIMENTAL*. [optional] if omitted the server will use the default value of False # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -235,7 +235,7 @@ def __init__(self, name, source, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) force (bool): [optional] if omitted the server will use the default value of False # noqa: E501 - hidden (bool): When set, branch will not show up when listing branches by default. [optional] if omitted the server will use the default value of False # noqa: E501 + hidden (bool): When set, branch will not show up when listing branches by default. *EXPERIMENTAL*. [optional] if omitted the server will use the default value of False # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) diff --git a/clients/python/docs/BranchCreation.md b/clients/python/docs/BranchCreation.md index 6be9e382d83..13c9fc90535 100644 --- a/clients/python/docs/BranchCreation.md +++ b/clients/python/docs/BranchCreation.md @@ -8,7 +8,7 @@ Name | Type | Description | Notes **name** | **str** | | **source** | **str** | | **force** | **bool** | | [optional] [default to False] -**hidden** | **bool** | When set, branch will not show up when listing branches by default | [optional] [default to False] +**hidden** | **bool** | When set, branch will not show up when listing branches by default. *EXPERIMENTAL* | [optional] [default to False] ## Example diff --git a/clients/python/lakefs_sdk/models/branch_creation.py b/clients/python/lakefs_sdk/models/branch_creation.py index 679c2353274..d20faf88635 100644 --- a/clients/python/lakefs_sdk/models/branch_creation.py +++ b/clients/python/lakefs_sdk/models/branch_creation.py @@ -32,7 +32,7 @@ class BranchCreation(BaseModel): name: StrictStr = Field(...) source: StrictStr = Field(...) force: Optional[StrictBool] = False - hidden: Optional[StrictBool] = Field(False, description="When set, branch will not show up when listing branches by default") + hidden: Optional[StrictBool] = Field(False, description="When set, branch will not show up when listing branches by default. *EXPERIMENTAL*") __properties = ["name", "source", "force", "hidden"] class Config: diff --git a/clients/rust/docs/BranchCreation.md b/clients/rust/docs/BranchCreation.md index c08b639c9df..b06f9cf3920 100644 --- a/clients/rust/docs/BranchCreation.md +++ b/clients/rust/docs/BranchCreation.md @@ -7,7 +7,7 @@ Name | Type | Description | Notes **name** | **String** | | **source** | **String** | | **force** | Option<**bool**> | | [optional][default to false] -**hidden** | Option<**bool**> | When set, branch will not show up when listing branches by default | [optional][default to false] +**hidden** | Option<**bool**> | When set, branch will not show up when listing branches by default. *EXPERIMENTAL* | [optional][default to false] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/clients/rust/src/models/branch_creation.rs b/clients/rust/src/models/branch_creation.rs index 80942b00314..be0e71f887e 100644 --- a/clients/rust/src/models/branch_creation.rs +++ b/clients/rust/src/models/branch_creation.rs @@ -18,7 +18,7 @@ pub struct BranchCreation { pub source: String, #[serde(rename = "force", skip_serializing_if = "Option::is_none")] pub force: Option, - /// When set, branch will not show up when listing branches by default + /// When set, branch will not show up when listing branches by default. *EXPERIMENTAL* #[serde(rename = "hidden", skip_serializing_if = "Option::is_none")] pub hidden: Option, } diff --git a/docs/assets/js/swagger.yml b/docs/assets/js/swagger.yml index 3c7810590a5..37f55b2ae8f 100644 --- a/docs/assets/js/swagger.yml +++ b/docs/assets/js/swagger.yml @@ -695,7 +695,7 @@ components: default: false hidden: type: boolean - description: When set, branch will not show up when listing branches by default + description: When set, branch will not show up when listing branches by default. *EXPERIMENTAL* default: false TagCreation: diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index b32a952c8aa..e70dac413c0 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -1502,8 +1502,9 @@ func TestController_ListBranchesHandler(t *testing.T) { if len(results) != 2 { t.Fatalf("expected 2 branches to return, got %d", len(results)) } - require.Equal(t, results[0].Id, "main3") - require.Equal(t, results[1].Id, "main5") + expectedRefs := []string{"main3", "main5"} + gotRefs := []string{results[0].Id, results[1].Id} + require.Equal(t, expectedRefs, gotRefs) // List all branches resp, err = clt.ListBranchesWithResponse(ctx, repo, &apigen.ListBranchesParams{ @@ -1516,8 +1517,9 @@ func TestController_ListBranchesHandler(t *testing.T) { if len(results) != 2 { t.Fatalf("expected 2 branches to return, got %d", len(results)) } - require.Equal(t, results[0].Id, "main2") - require.Equal(t, results[1].Id, "main3") + expectedRefs = []string{"main2", "main3"} + gotRefs = []string{results[0].Id, results[1].Id} + require.Equal(t, expectedRefs, gotRefs) }) t.Run("list branches repo doesnt exist", func(t *testing.T) { From 4a48b437cc4f18a639f196b6015d88aaa9c70647 Mon Sep 17 00:00:00 2001 From: Nir Ozery Date: Fri, 22 Nov 2024 10:56:32 -0500 Subject: [PATCH 5/5] CR Fixes 3 --- api/swagger.yml | 2 +- clients/java-legacy/api/openapi.yaml | 2 +- clients/java/api/openapi.yaml | 2 +- docs/assets/js/swagger.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/swagger.yml b/api/swagger.yml index 37f55b2ae8f..e4e16d94b0f 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -3553,7 +3553,7 @@ paths: schema: type: boolean default: false - description: When set - list all branches including hidden branches + description: When set - list all branches including hidden branches. *EXPERIMENTAL* responses: 200: description: branch list diff --git a/clients/java-legacy/api/openapi.yaml b/clients/java-legacy/api/openapi.yaml index 9a8530fcf62..6f49945807d 100644 --- a/clients/java-legacy/api/openapi.yaml +++ b/clients/java-legacy/api/openapi.yaml @@ -3121,7 +3121,7 @@ paths: required: false schema: default: false - description: When set - list all branches including hidden branches + description: When set - list all branches including hidden branches. *EXPERIMENTAL* type: boolean style: form responses: diff --git a/clients/java/api/openapi.yaml b/clients/java/api/openapi.yaml index cfe8a2a3452..01b85b7b499 100644 --- a/clients/java/api/openapi.yaml +++ b/clients/java/api/openapi.yaml @@ -3121,7 +3121,7 @@ paths: required: false schema: default: false - description: When set - list all branches including hidden branches + description: When set - list all branches including hidden branches. *EXPERIMENTAL* type: boolean style: form responses: diff --git a/docs/assets/js/swagger.yml b/docs/assets/js/swagger.yml index 37f55b2ae8f..e4e16d94b0f 100644 --- a/docs/assets/js/swagger.yml +++ b/docs/assets/js/swagger.yml @@ -3553,7 +3553,7 @@ paths: schema: type: boolean default: false - description: When set - list all branches including hidden branches + description: When set - list all branches including hidden branches. *EXPERIMENTAL* responses: 200: description: branch list