Unverified Commit e311cac6 authored by Arnaud Lefebvre's avatar Arnaud Lefebvre
Browse files

Groups: add support for custom attributes

Fix #769
parent 37617ec3
......@@ -3,6 +3,7 @@ package org.gitlab4j.api;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
......@@ -15,6 +16,7 @@ import org.gitlab4j.api.models.AccessLevel;
import org.gitlab4j.api.models.AccessRequest;
import org.gitlab4j.api.models.AuditEvent;
import org.gitlab4j.api.models.Badge;
import org.gitlab4j.api.models.CustomAttribute;
import org.gitlab4j.api.models.Group;
import org.gitlab4j.api.models.GroupFilter;
import org.gitlab4j.api.models.GroupParams;
......@@ -1725,4 +1727,123 @@ public class GroupApi extends AbstractApi {
delete(Response.Status.NO_CONTENT, null,
"groups", getGroupIdOrPath(groupIdOrPath), "share", sharedWithGroupId);
}
/**
* Get all custom attributes for the specified group.
*
* <pre><code>GitLab Endpoint: GET /groups/:id/custom_attributes</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @return a list of group's CustomAttributes
* @throws GitLabApiException if any exception occurs
*/
public List<CustomAttribute> getCustomAttributes(final Object groupIdOrPath) throws GitLabApiException {
return (getCustomAttributes(groupIdOrPath, getDefaultPerPage()).all());
}
/**
* Get a Pager of custom attributes for the specified group.
*
* <pre><code>GitLab Endpoint: GET /groups/:id/custom_attributes</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @param itemsPerPage the number of items per page
* @return a Pager of group's custom attributes
* @throws GitLabApiException if any exception occurs
*/
public Pager<CustomAttribute> getCustomAttributes(final Object groupIdOrPath, int itemsPerPage) throws GitLabApiException {
return (new Pager<CustomAttribute>(this, CustomAttribute.class, itemsPerPage, null,
"groups", getGroupIdOrPath(groupIdOrPath), "custom_attributes"));
}
/**
* Get a Stream of all custom attributes for the specified group.
*
* <pre><code>GitLab Endpoint: GET /groups/:id/custom_attributes</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @return a Stream of group's custom attributes
* @throws GitLabApiException if any exception occurs
*/
public Stream<CustomAttribute> getCustomAttributesStream(final Object groupIdOrPath) throws GitLabApiException {
return (getCustomAttributes(groupIdOrPath, getDefaultPerPage()).stream());
}
/**
* Get a single custom attribute for the specified group.
*
* <pre><code>GitLab Endpoint: GET /groups/:id/custom_attributes/:key</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @param key the key for the custom attribute
* @return a CustomAttribute instance for the specified key
* @throws GitLabApiException if any exception occurs
*/
public CustomAttribute getCustomAttribute(final Object groupIdOrPath, final String key) throws GitLabApiException {
Response response = get(Response.Status.OK, null,
"groups", getGroupIdOrPath(groupIdOrPath), "custom_attributes", key);
return (response.readEntity(CustomAttribute.class));
}
/**
* Get an Optional instance with the value for a single custom attribute for the specified group.
*
* <pre><code>GitLab Endpoint: GET /groups/:id/custom_attributes/:key</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance, required
* @param key the key for the custom attribute, required
* @return an Optional instance with the value for a single custom attribute for the specified group
*/
public Optional<CustomAttribute> geOptionalCustomAttribute(final Object groupIdOrPath, final String key) {
try {
return (Optional.ofNullable(getCustomAttribute(groupIdOrPath, key)));
} catch (GitLabApiException glae) {
return (GitLabApi.createOptionalFromException(glae));
}
}
/**
* Set a custom attribute for the specified group. The attribute will be updated if it already exists,
* or newly created otherwise.
*
* <pre><code>GitLab Endpoint: PUT /groups/:id/custom_attributes/:key</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @param key the key for the custom attribute
* @param value the value for the customAttribute
* @return a CustomAttribute instance for the updated or created custom attribute
* @throws GitLabApiException if any exception occurs
*/
public CustomAttribute setCustomAttribute(final Object groupIdOrPath, final String key, final String value) throws GitLabApiException {
if (Objects.isNull(key) || key.trim().isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty");
}
if (Objects.isNull(value) || value.trim().isEmpty()) {
throw new IllegalArgumentException("Value cannot be null or empty");
}
GitLabApiForm formData = new GitLabApiForm().withParam("value", value);
Response response = putWithFormData(Response.Status.OK, formData,
"groups", getGroupIdOrPath(groupIdOrPath), "custom_attributes", key);
return (response.readEntity(CustomAttribute.class));
}
/**
* Delete a custom attribute for the specified group.
*
* <pre><code>GitLab Endpoint: DELETE /groups/:id/custom_attributes/:key</code></pre>
*
* @param groupIdOrPath the group in the form of an Integer(ID), String(path), or Group instance
* @param key the key of the custom attribute to delete
* @throws GitLabApiException if any exception occurs
*/
public void deleteCustomAttribute(final Object groupIdOrPath, final String key) throws GitLabApiException {
if (Objects.isNull(key) || key.trim().isEmpty()) {
throw new IllegalArgumentException("Key can't be null or empty");
}
delete(Response.Status.OK, null, "groups", getGroupIdOrPath(groupIdOrPath), "custom_attributes", key);
}
}
......@@ -5,6 +5,7 @@ import java.util.Map;
import java.util.WeakHashMap;
import org.gitlab4j.api.GitLabApi.ApiVersion;
import org.gitlab4j.api.models.Group;
import org.gitlab4j.api.models.Project;
import org.gitlab4j.api.models.User;
......@@ -16,6 +17,7 @@ import org.gitlab4j.api.models.User;
* TEST_PRIVATE_TOKEN
* TEST_NAMESPACE
* TEST_PROJECT_NAME
* TEST_GROUP
*/
public class AbstractIntegrationTest implements PropertyConstants {
......@@ -33,6 +35,7 @@ public class AbstractIntegrationTest implements PropertyConstants {
protected static class BaseTestResources {
protected GitLabApi gitLabApi;
protected Project testProject;
protected Group testGroup;
protected BaseTestResources(GitLabApi gitLabApi) {
this.gitLabApi = gitLabApi;
......@@ -80,6 +83,10 @@ public class AbstractIntegrationTest implements PropertyConstants {
problems += "TEST_PROJECT_NAME cannot be empty\n";
}
if (TEST_GROUP == null || TEST_GROUP.trim().isEmpty()) {
problems += "TEST_GROUP cannot be empty\n";
}
if (problems.isEmpty()) {
try {
GitLabApi gitLabApi = new GitLabApi(ApiVersion.V4, TEST_HOST_URL, TEST_PRIVATE_TOKEN);
......@@ -123,6 +130,34 @@ public class AbstractIntegrationTest implements PropertyConstants {
}
}
/**
* Get the test Group instance for the calling test class.
*
* @return the test Group instance for the calling test class
*/
protected static Group getTestGroup() {
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
String callingClassName = directCaller.getClassName();
BaseTestResources baseResources = baseTestResourcesMap.get(callingClassName);
if (baseResources == null || baseResources.gitLabApi == null) {
System.err.println("Problems fetching test Project instance: GitLabApi instance is null");
return (null);
} else if (baseResources.testGroup != null) {
return (baseResources.testGroup);
}
try {
Group testGroup = (baseResources.gitLabApi.getGroupApi().getGroup(TEST_GROUP));
baseResources.testGroup = testGroup;
return (testGroup);
} catch (Exception e) {
System.err.println("Problems fetching test Project instance: " + e.getMessage());
return (null);
}
}
/**
* Get the current user (the testing user).
*
......
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Greg Messner <greg@messners.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.gitlab4j.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.gitlab4j.api.models.CustomAttribute;
import org.gitlab4j.api.models.Group;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* In order for these tests to run you must set the following properties in ~/test-gitlab4j.properties
*
* TEST_NAMESPACE
* TEST_GROUP
* TEST_HOST_URL
* TEST_PRIVATE_TOKEN
*
* If any of the above are NULL, all tests in this class will be skipped.
*/
@Category(IntegrationTest.class)
public class TestGroupCustomAttributes extends AbstractIntegrationTest {
private static final String TEST_CUSTOM_ATTRIBUTE_KEY = "GitLab4JCustomAttributeTestKey";
private static final String TEST_CUSTOM_ATTRIBUTE_VALUE = "CustomAttributeValue";
private static GitLabApi gitLabApi;
private static Group testGroup;
public TestGroupCustomAttributes() {
super();
}
@BeforeClass
public static void setup() {
// Must setup the connection to the GitLab test server and get the test Group instance
gitLabApi = baseTestSetup();
testGroup = getTestGroup();
deleteAllTestCustomAttributes();
}
@AfterClass
public static void teardown() throws GitLabApiException {
deleteAllTestCustomAttributes();
}
private static void deleteAllTestCustomAttributes() {
if (gitLabApi != null) {
try {
List<CustomAttribute> customAttributes = gitLabApi.getGroupApi().getCustomAttributes(testGroup);
if (customAttributes != null) {
for (CustomAttribute customAttribute : customAttributes) {
if (customAttribute.getKey().startsWith(TEST_CUSTOM_ATTRIBUTE_KEY)) {
gitLabApi.getGroupApi().deleteCustomAttribute(testGroup, customAttribute.getKey());
}
}
}
} catch (GitLabApiException ignore) {
}
}
}
@Before
public void beforeMethod() {
assumeNotNull(gitLabApi);
}
private CustomAttribute createCustomAttribute(String key, String value) throws GitLabApiException {
return (gitLabApi.getGroupApi().setCustomAttribute(testGroup, key, value));
}
@Test
public void testCreate() throws GitLabApiException {
CustomAttribute customAttribute = createCustomAttribute(TEST_CUSTOM_ATTRIBUTE_KEY, TEST_CUSTOM_ATTRIBUTE_VALUE);
assertNotNull(customAttribute);
assertEquals(TEST_CUSTOM_ATTRIBUTE_KEY, customAttribute.getKey());
assertEquals(TEST_CUSTOM_ATTRIBUTE_VALUE, customAttribute.getValue());
}
@Test
public void testUpdate() throws GitLabApiException {
assumeNotNull(testGroup);
String key = TEST_CUSTOM_ATTRIBUTE_KEY + "TestUpdate";
String value = TEST_CUSTOM_ATTRIBUTE_VALUE;
CustomAttribute customAttribute = createCustomAttribute(key, value);
assertNotNull(customAttribute);
assertEquals(key, customAttribute.getKey());
assertEquals(value, customAttribute.getValue());
value = TEST_CUSTOM_ATTRIBUTE_VALUE + " (updated)";
customAttribute = gitLabApi.getGroupApi().setCustomAttribute(testGroup, key, value);
assertEquals(key, customAttribute.getKey());
assertEquals(value, customAttribute.getValue());
}
@Test
public void testGetCustomAttribute() throws GitLabApiException {
assumeNotNull(testGroup);
String key = TEST_CUSTOM_ATTRIBUTE_KEY + "TestGet";
String value = TEST_CUSTOM_ATTRIBUTE_VALUE + " (test get)";
CustomAttribute newCustomAttribute = createCustomAttribute(key, value);
assertNotNull(newCustomAttribute);
Optional<CustomAttribute> customAttribute =
gitLabApi.getGroupApi().geOptionalCustomAttribute(testGroup, key);
assertTrue(customAttribute.isPresent());
assertEquals(key, customAttribute.get().getKey());
assertEquals(value, customAttribute.get().getValue());
}
@Test
public void testListCustomAttributes() throws GitLabApiException {
assumeNotNull(testGroup);
String key = TEST_CUSTOM_ATTRIBUTE_KEY + "TestList";
String value = TEST_CUSTOM_ATTRIBUTE_VALUE + " (test list)";
CustomAttribute newCustomAttribute = createCustomAttribute(key, value);
assertNotNull(newCustomAttribute);
List<CustomAttribute> customAttributes = gitLabApi.getGroupApi().getCustomAttributes(testGroup);
assertNotNull(customAttributes);
for (CustomAttribute customAttribute : customAttributes) {
if (key.equals(customAttribute.getKey())) {
assertEquals(value, customAttribute.getValue());
break;
}
}
}
@Test
public void testDeleteCustomAttribute() throws GitLabApiException {
assumeNotNull(testGroup);
String key = TEST_CUSTOM_ATTRIBUTE_KEY + "TestDelete";
String value = TEST_CUSTOM_ATTRIBUTE_VALUE + " (test delete)";
createCustomAttribute(key, value);
Stream<CustomAttribute> stream = gitLabApi.getGroupApi().getCustomAttributesStream(testGroup);
Optional<CustomAttribute> match = stream.filter(c -> c.getKey().equals(key)).findFirst();
assertTrue(match.isPresent());
gitLabApi.getGroupApi().deleteCustomAttribute(testGroup, key);
stream = gitLabApi.getGroupApi().getCustomAttributesStream(testGroup);
match = stream.filter(c -> c.getKey().equals(key)).findFirst();
assertFalse(match.isPresent());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment