Support swapping default roles (#2868)

* Support swapping default roles

Pseudocode:

```
A. patchRole(role1, default=True):
B.   set role1.default = True
     for all users:
       add role1 to user.roles
C.   for role in roles if role != role1:
       set role.default = False
       for all users:
         delete role from user.roles
```

This ensures that changeDescription for the role(s) and user(s) are updated accordingly.

Potential optimization:
Adding role1 and removing role from user.roles could be considered/implemented as one change.
However, increases code complexity.

* Set DataConsumer as default role

* Fix tests

* Fix code smell
This commit is contained in:
Matt 2022-02-20 11:27:32 -08:00 committed by GitHub
parent 5419366a43
commit 9e4d8d709d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 267 additions and 58 deletions

View File

@ -42,11 +42,15 @@ CREATE TABLE IF NOT EXISTS glossary_term_entity (
);
-- Set default as false for all existing roles, to avoid unintended manipulation of roles during migration.
-- Set default as false for all existing roles except DataConsumer, to avoid unintended manipulation of roles during migration.
UPDATE role_entity
SET json = JSON_SET(json, '$.default', FALSE);
UPDATE role_entity
SET json = JSON_SET(json, '$.default', TRUE)
WHERE name = 'DataConsumer';
ALTER TABLE role_entity
ADD COLUMN `default` BOOLEAN GENERATED ALWAYS AS (JSON_EXTRACT(json, '$.default')),
ADD INDEX(`default`);

View File

@ -29,6 +29,10 @@ public final class CatalogExceptionMessage {
return entityNotFound(entityType, id.toString());
}
public static String entitiesNotFound(String entityType) {
return String.format("%s instances not found", entityType);
}
public static String readOnlyAttribute(String entityType, String attribute) {
return String.format("%s attribute %s can't be modified", entityType, attribute);
}

View File

@ -908,12 +908,12 @@ public abstract class EntityRepository<T> {
this.operation = operation;
}
public final void update() throws IOException {
public final void update() throws IOException, ParseException {
update(false);
}
/** Compare original and updated entities and perform updates. Update the entity version and track changes. */
public final void update(boolean allowEdits) throws IOException {
public final void update(boolean allowEdits) throws IOException, ParseException {
if (operation.isDelete()) { // DELETE Operation
updateDeleted();
} else { // PUT or PATCH operations
@ -930,7 +930,7 @@ public abstract class EntityRepository<T> {
storeUpdate();
}
public void entitySpecificUpdate() throws IOException {
public void entitySpecificUpdate() throws IOException, ParseException {
// Default implementation. Override this to add any entity specific field updates
}

View File

@ -16,12 +16,15 @@ package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.ws.rs.core.UriInfo;
@ -31,10 +34,13 @@ import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.catalog.Entity;
import org.openmetadata.catalog.entity.policies.Policy;
import org.openmetadata.catalog.entity.teams.Role;
import org.openmetadata.catalog.entity.teams.User;
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
import org.openmetadata.catalog.exception.EntityNotFoundException;
import org.openmetadata.catalog.resources.teams.RoleResource;
import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.type.PolicyType;
import org.openmetadata.catalog.type.Relationship;
import org.openmetadata.catalog.util.EntityInterface;
@ -180,24 +186,16 @@ public class RoleRepository extends EntityRepository<Role> {
}
public ResultList<Role> getDefaultRolesResultList(UriInfo uriInfo, Fields fields)
throws GeneralSecurityException, UnsupportedEncodingException {
throws GeneralSecurityException, IOException {
List<Role> roles = getDefaultRoles(uriInfo, fields);
return new ResultList<>(roles, null, null, roles.size());
}
private List<Role> getDefaultRoles(UriInfo uriInfo, Fields fields) {
List<Role> roles =
daoCollection.roleDAO().getDefaultRoles().stream()
.map(
json -> {
try {
return withHref(uriInfo, setFields(JsonUtils.readValue(json, Role.class), fields));
} catch (IOException e) {
LOG.warn("Could not parse Role from json {}", json);
}
return null;
})
.collect(Collectors.toList());
private List<Role> getDefaultRoles(UriInfo uriInfo, Fields fields) throws IOException {
List<Role> roles = new ArrayList<>();
for (String roleJson : daoCollection.roleDAO().getDefaultRoles()) {
roles.add(withHref(uriInfo, setFields(JsonUtils.readValue(roleJson, Role.class), fields)));
}
if (roles.size() > 1) {
LOG.warn(
"{} roles {}, are registered as default. There SHOULD be only one role marked as default.",
@ -340,8 +338,97 @@ public class RoleRepository extends EntityRepository<Role> {
}
@Override
public void entitySpecificUpdate() throws IOException {
recordChange("default", original.getEntity().getDefault(), updated.getEntity().getDefault());
public void entitySpecificUpdate() throws IOException, ParseException {
updateDefault(original.getEntity(), updated.getEntity());
}
private void updateDefault(Role origRole, Role updatedRole) throws IOException, ParseException {
long startTime = System.nanoTime();
if (Boolean.FALSE.equals(origRole.getDefault()) && Boolean.TRUE.equals(updatedRole.getDefault())) {
setDefaultToTrue(updatedRole);
}
if (Boolean.TRUE.equals(origRole.getDefault()) && Boolean.FALSE.equals(updatedRole.getDefault())) {
setDefaultToFalse(updatedRole);
}
recordChange("default", origRole.getDefault(), updatedRole.getDefault());
LOG.debug(
"Took {} ns to update {} role field default from {} to {}",
System.nanoTime() - startTime,
updatedRole.getName(),
origRole.getDefault(),
updatedRole.getDefault());
}
private void setDefaultToTrue(Role role) throws IOException, ParseException {
List<Role> defaultRoles = getDefaultRoles(null, ROLE_PATCH_FIELDS);
EntityRepository<Role> roleRepository = Entity.getEntityRepository(Entity.ROLE);
// Set default=FALSE for all existing default roles.
for (Role defaultRole : defaultRoles) {
if (defaultRole.getId().equals(role.getId())) {
// Skip the current role which is being set with default=TRUE.
continue;
}
Role origDefaultRole = roleRepository.get(null, defaultRole.getId().toString(), ROLE_PATCH_FIELDS);
Role updatedDefaultRole = roleRepository.get(null, defaultRole.getId().toString(), ROLE_PATCH_FIELDS);
updatedDefaultRole = updatedDefaultRole.withDefault(false);
new RoleUpdater(origDefaultRole, updatedDefaultRole, Operation.PATCH).update();
}
LOG.info("Creating 'user --- has ---> role' relationship for {} role", role.getName());
updateUsers(new RoleEntityInterface(role).getEntityReference(), null);
}
private void setDefaultToFalse(Role role) {
LOG.info("Deleting 'user --- has ---> role' relationship for {} role", role.getName());
updateUsers(null, new RoleEntityInterface(role).getEntityReference());
}
private List<User> getAllUsers() {
EntityRepository<User> userRepository = Entity.getEntityRepository(Entity.USER);
try {
return userRepository
.listAfter(null, UserRepository.USER_UPDATE_FIELDS, null, Integer.MAX_VALUE - 1, null, Include.ALL)
.getData();
} catch (GeneralSecurityException | IOException | ParseException e) {
throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entitiesNotFound(Entity.USER));
}
}
private void updateUsers(EntityReference addRole, EntityReference removeRole) {
List<User> users = getAllUsers();
if (users.isEmpty()) {
return;
}
EntityRepository<User> userRepository = Entity.getEntityRepository(Entity.USER);
users
.parallelStream()
.forEach(
user -> {
try {
updateUser(userRepository, user, addRole, removeRole);
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}
});
}
private void updateUser(
EntityRepository<User> userRepository, User user, EntityReference addRole, EntityReference removeRole)
throws IOException, ParseException {
User origUser = userRepository.get(null, user.getId().toString(), UserRepository.USER_PATCH_FIELDS);
User updatedUser = userRepository.get(null, user.getId().toString(), UserRepository.USER_PATCH_FIELDS);
List<EntityReference> roles = updatedUser.getRoles();
if (roles == null) {
roles = new ArrayList<>();
}
Set<EntityReference> rolesSet = new HashSet<>(roles);
if (addRole != null) {
rolesSet.add(addRole);
}
if (removeRole != null) {
rolesSet.remove(removeRole);
}
updatedUser.setRoles(new ArrayList<>(rolesSet));
Entity.getEntityRepository(Entity.USER).getUpdater(origUser, updatedUser, Operation.PATCH).update();
}
}
}

View File

@ -78,7 +78,7 @@ public class UserRepository extends EntityRepository<User> {
daoCollection.roleDAO().getDefaultRolesIds().stream().map(UUID::fromString).collect(Collectors.toSet());
defaultRoleIds.removeAll(existingRoleIds);
if (rolesRef == null || rolesRef.size() == 0) {
if (rolesRef == null || rolesRef.isEmpty()) {
rolesRef = new ArrayList<>();
}
for (UUID roleId : defaultRoleIds) {

View File

@ -417,21 +417,22 @@ public class WebhookRepository extends EntityRepository<Webhook> {
webhook.getEventFilters().forEach(f -> filter.put(f.getEventType(), f.getEntities()));
}
private void setErrorStatus(Long attemptTime, Integer statusCode, String reason) throws IOException {
private void setErrorStatus(Long attemptTime, Integer statusCode, String reason)
throws IOException, ParseException {
if (!attemptTime.equals(webhook.getFailureDetails().getLastFailedAt())) {
setStatus(Status.FAILED, attemptTime, statusCode, reason, null);
}
throw new RuntimeException(reason);
}
private void setAwaitingRetry(Long attemptTime, int statusCode, String reason) throws IOException {
private void setAwaitingRetry(Long attemptTime, int statusCode, String reason) throws IOException, ParseException {
if (!attemptTime.equals(webhook.getFailureDetails().getLastFailedAt())) {
setStatus(Status.AWAITING_RETRY, attemptTime, statusCode, reason, attemptTime + currentBackoffTime);
}
}
private void setStatus(Status status, Long attemptTime, Integer statusCode, String reason, Long timestamp)
throws IOException {
throws IOException, ParseException {
Webhook stored = daoCollection.webhookDAO().findEntityById(webhook.getId());
webhook.setStatus(status);
webhook

View File

@ -2,5 +2,6 @@
"name": "DataConsumer",
"displayName": "Data Consumer",
"description": "Users with Data Consumer role use different data assets for their day to day work.",
"deleted": false
"deleted": false,
"default": true
}

View File

@ -1372,6 +1372,18 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
}
public final T createAndCheckEntity(K create, Map<String, String> authHeaders) throws IOException {
return createAndCheckEntity(create, authHeaders, create);
}
/**
* Helper function to create an entity, submit POST API request and validate response.
*
* @param create entity to be created
* @param authHeaders auth headers to be used for the PATCH API request
* @param created expected response from POST API after entity has been created
* @return entity response from the POST API
*/
public final T createAndCheckEntity(K create, Map<String, String> authHeaders, K created) throws IOException {
// Validate an entity that is created has all the information set in create request
String updatedBy = TestUtils.getPrincipal(authHeaders);
T entity = createEntity(create, authHeaders);
@ -1379,12 +1391,12 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
assertEquals(updatedBy, entityInterface.getUpdatedBy());
assertEquals(0.1, entityInterface.getVersion()); // First version of the entity
validateCreatedEntity(entity, create, authHeaders);
validateCreatedEntity(entity, created, authHeaders);
// GET the entity created and ensure it has all the information set in create request
T getEntity = getEntity(entityInterface.getId(), authHeaders);
assertEquals(0.1, entityInterface.getVersion()); // First version of the entity
validateCreatedEntity(getEntity, create, authHeaders);
validateCreatedEntity(getEntity, created, authHeaders);
// TODO GET the entity by name
@ -1465,10 +1477,32 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
UpdateType updateType,
ChangeDescription expectedChange)
throws IOException {
return patchEntityAndCheck(updated, originalJson, authHeaders, updateType, expectedChange, updated);
}
/**
* Helper function to generate JSON PATCH, submit PATCH API request and validate response.
*
* @param updated entity to compare with response from PATCH API
* @param originalJson JSON representation of entity before the update
* @param authHeaders auth headers to be used for the PATCH API request
* @param updateType type of update, see {@link TestUtils.UpdateType}
* @param expectedChange change description that is expected from the PATCH API response
* @param update entity used to diff against originalJson to generate JSON PATCH for PATCH API test
* @return entity response from the PATCH API
*/
protected final T patchEntityAndCheck(
T updated,
String originalJson,
Map<String, String> authHeaders,
UpdateType updateType,
ChangeDescription expectedChange,
T update)
throws IOException {
EntityInterface<T> entityInterface = getEntityInterface(updated);
// Validate information returned in patch response has the updates
T returned = patchEntity(entityInterface.getId(), originalJson, updated, authHeaders);
T returned = patchEntity(entityInterface.getId(), originalJson, update, authHeaders);
entityInterface = getEntityInterface(returned);
compareEntities(updated, returned, authHeaders);

View File

@ -15,6 +15,8 @@ package org.openmetadata.catalog.resources.teams;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.openmetadata.catalog.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.catalog.util.TestUtils.TEST_AUTH_HEADERS;
import static org.openmetadata.catalog.util.TestUtils.UpdateType.MINOR_UPDATE;
@ -36,6 +38,7 @@ import org.openmetadata.catalog.Entity;
import org.openmetadata.catalog.api.teams.CreateRole;
import org.openmetadata.catalog.entity.policies.Policy;
import org.openmetadata.catalog.entity.teams.Role;
import org.openmetadata.catalog.entity.teams.User;
import org.openmetadata.catalog.jdbi3.RoleRepository.RoleEntityInterface;
import org.openmetadata.catalog.resources.EntityResourceTest;
import org.openmetadata.catalog.resources.policies.PolicyResource;
@ -56,11 +59,47 @@ public class RoleResourceTest extends EntityResourceTest<Role, CreateRole> {
}
@Test
void get_queryDefaultRole(TestInfo test) throws IOException {
Role defaultRole = createRolesAndSetDefault(test, 7);
void defaultRole(TestInfo test) throws IOException {
// Check if there is a default role in the beginning. DataConsumer is a default role.
List<Role> defaultRoles = getDefaultRoles();
assertEquals(1, defaultRoles.size());
assertEquals(defaultRole.getId(), defaultRoles.get(0).getId());
Role prevDefaultRole = defaultRoles.get(0);
// Update the default role and check the following:
// - the default role has changed
// - the previous default role is no longer set as default
// - all users have roles set correctly (only current default role should exist)
for (int i = 0; i < 2; i++) { // Run two iterations for this test.
Role defaultRole = createRolesAndSetDefault(test, 7, i * 10);
defaultRoles = getDefaultRoles();
assertEquals(1, defaultRoles.size());
assertEquals(defaultRole.getId(), defaultRoles.get(0).getId());
assertNotEquals(defaultRole.getId(), prevDefaultRole.getId());
List<User> users = new UserResourceTest().listEntities(Map.of("fields", "roles"), ADMIN_AUTH_HEADERS).getData();
for (User user : users) {
boolean defaultRoleSet = false, prevDefaultRoleExists = false;
for (EntityReference role : user.getRoles()) {
if (role.getId().equals(defaultRole.getId())) {
defaultRoleSet = true;
}
if (role.getId().equals(prevDefaultRole.getId())) {
prevDefaultRoleExists = true;
}
}
if (!defaultRoleSet) {
fail(String.format("Default role %s was not set for user %s", defaultRole.getName(), user.getName()));
}
if (prevDefaultRoleExists) {
fail(
String.format(
"Previous default role %s has not been removed for user %s",
prevDefaultRole.getName(), user.getName()));
}
}
prevDefaultRole = defaultRole;
}
}
public List<Role> getDefaultRoles() throws HttpResponseException {
@ -72,17 +111,18 @@ public class RoleResourceTest extends EntityResourceTest<Role, CreateRole> {
*
* @return the default role
*/
public Role createRolesAndSetDefault(TestInfo test, @Positive int numberOfRoles) throws IOException {
public Role createRolesAndSetDefault(TestInfo test, @Positive int numberOfRoles, @Positive int offset)
throws IOException {
// Create a set of roles.
for (int i = 0; i < numberOfRoles; i++) {
CreateRole create = createRequest(test, i + 1);
CreateRole create = createRequest(test, offset + i + 1);
createAndCheckRole(create, ADMIN_AUTH_HEADERS);
}
// Set one of the roles as default.
Role role =
getEntityByName(
getEntityName(test, new Random().nextInt(numberOfRoles)),
getEntityName(test, offset + new Random().nextInt(numberOfRoles)),
Collections.emptyMap(),
RoleResource.FIELDS,
ADMIN_AUTH_HEADERS);

View File

@ -101,19 +101,15 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
@Order(Integer.MAX_VALUE) // Run this test last to avoid side effects of default role creation to fail other tests.
@Test
void post_userWithDefaultRole(TestInfo test) throws IOException {
// Given no default role has been set, when a user is created, then no role should be assigned.
CreateUser create = createRequest(test, 1);
createUserAndCheckRoles(create, Collections.emptyList());
RoleResourceTest roleResourceTest = new RoleResourceTest();
Role defaultRole = roleResourceTest.createRolesAndSetDefault(test, 7);
List<Role> roles = roleResourceTest.listEntities(Collections.emptyMap(), ADMIN_AUTH_HEADERS).getData();
UUID nonDefaultRoleId = roles.stream().filter(role -> !role.getDefault()).findAny().orElseThrow().getId();
UUID defaultRoleId = defaultRole.getId();
UUID defaultRoleId =
roles.stream().filter(Role::getDefault).findAny().orElseThrow().getId(); // DataConsumer is default role.
// Given a default role has been set, when a user is created without any roles, then the default role should be
// assigned.
create = createRequest(test, 2);
CreateUser create = createRequest(test, 2);
createUserAndCheckRoles(create, Arrays.asList(defaultRoleId));
// Given a default role has been set, when a user is created with a non default role, then the default role should
@ -244,12 +240,24 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
RoleResourceTest roleResourceTest = new RoleResourceTest();
Role role1 = roleResourceTest.createEntity(roleResourceTest.createRequest(test, 1), ADMIN_AUTH_HEADERS);
Role role2 = roleResourceTest.createEntity(roleResourceTest.createRequest(test, 2), ADMIN_AUTH_HEADERS);
UUID defaultRoleId =
roleResourceTest.listEntities(Collections.emptyMap(), ADMIN_AUTH_HEADERS).getData().stream()
.filter(Role::getDefault)
.findAny()
.orElseThrow()
.getId(); // DataConsumer is default role.
EntityReference defaultRoleRef =
new RoleEntityInterface(roleResourceTest.getEntity(defaultRoleId, RoleResource.FIELDS, ADMIN_AUTH_HEADERS))
.getEntityReference();
List<UUID> roles = Arrays.asList(role1.getId(), role2.getId());
CreateUser create = createRequest(test).withRoles(roles);
User user = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
List<UUID> createdRoles = Arrays.asList(role1.getId(), role2.getId(), defaultRoleRef.getId());
CreateUser created = createRequest(test).withRoles(createdRoles);
User user = createAndCheckEntity(create, ADMIN_AUTH_HEADERS, created);
// Ensure User has relationship to these roles
String[] expectedRoles = roles.stream().map(UUID::toString).sorted().toArray(String[]::new);
String[] expectedRoles = createdRoles.stream().map(UUID::toString).sorted().toArray(String[]::new);
List<EntityReference> roleReferences = user.getRoles();
String[] actualRoles = roleReferences.stream().map(ref -> ref.getId().toString()).sorted().toArray(String[]::new);
assertArrayEquals(expectedRoles, actualRoles);
@ -391,11 +399,16 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
Profile profile = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com")));
RoleResourceTest roleResourceTest = new RoleResourceTest();
List<Role> roles = roleResourceTest.listEntities(Collections.emptyMap(), ADMIN_AUTH_HEADERS).getData();
UUID defaultRoleId =
roles.stream().filter(Role::getDefault).findAny().orElseThrow().getId(); // DataConsumer is default role.
EntityReference defaultRoleRef =
new RoleEntityInterface(roleResourceTest.getEntity(defaultRoleId, RoleResource.FIELDS, ADMIN_AUTH_HEADERS))
.getEntityReference();
EntityReference role1 =
new RoleEntityInterface(
roleResourceTest.createEntity(roleResourceTest.createRequest(test, 1), ADMIN_AUTH_HEADERS))
.getEntityReference();
List<EntityReference> roles1 = Arrays.asList(role1);
//
// Add previously absent attributes
@ -403,22 +416,31 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
String origJson = JsonUtils.pojoToJson(user);
String timezone = "America/Los_Angeles";
user.withRoles(roles1)
user.withRoles(Arrays.asList(role1, defaultRoleRef))
.withTeams(teams)
.withTimezone(timezone)
.withDisplayName("displayName")
.withProfile(profile)
.withIsBot(false)
.withIsAdmin(false);
User update =
JsonUtils.readValue(origJson, User.class)
.withRoles(Arrays.asList(role1))
.withTeams(teams)
.withTimezone(timezone)
.withDisplayName("displayName")
.withProfile(profile)
.withIsBot(false)
.withIsAdmin(false);
ChangeDescription change = getChangeDescription(user.getVersion());
change.getFieldsAdded().add(new FieldChange().withName("roles").withNewValue(roles1));
change.getFieldsAdded().add(new FieldChange().withName("roles").withNewValue(Arrays.asList(role1)));
change.getFieldsAdded().add(new FieldChange().withName("teams").withNewValue(teams));
change.getFieldsAdded().add(new FieldChange().withName("timezone").withNewValue(timezone));
change.getFieldsAdded().add(new FieldChange().withName("displayName").withNewValue("displayName"));
change.getFieldsAdded().add(new FieldChange().withName("profile").withNewValue(profile));
change.getFieldsAdded().add(new FieldChange().withName("isBot").withNewValue(false));
user = patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change);
user = patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change, update);
//
// Replace the attributes
@ -431,20 +453,28 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
new RoleEntityInterface(
roleResourceTest.createEntity(roleResourceTest.createRequest(test, 2), ADMIN_AUTH_HEADERS))
.getEntityReference();
List<EntityReference> roles2 = Arrays.asList(role2);
origJson = JsonUtils.pojoToJson(user);
user.withRoles(roles2)
user.withRoles(Arrays.asList(role2, defaultRoleRef))
.withTeams(teams1)
.withTimezone(timezone1)
.withDisplayName("displayName1")
.withProfile(profile1)
.withIsBot(true)
.withIsAdmin(false);
update =
JsonUtils.readValue(origJson, User.class)
.withRoles(Arrays.asList(role2))
.withTeams(teams1)
.withTimezone(timezone1)
.withDisplayName("displayName1")
.withProfile(profile1)
.withIsBot(true)
.withIsAdmin(false);
change = getChangeDescription(user.getVersion());
change.getFieldsDeleted().add(new FieldChange().withName("roles").withOldValue(roles1));
change.getFieldsAdded().add(new FieldChange().withName("roles").withNewValue(roles2));
change.getFieldsDeleted().add(new FieldChange().withName("roles").withOldValue(Arrays.asList(role1)));
change.getFieldsAdded().add(new FieldChange().withName("roles").withNewValue(Arrays.asList(role2)));
change.getFieldsDeleted().add(new FieldChange().withName("teams").withOldValue(List.of(team2)));
change.getFieldsAdded().add(new FieldChange().withName("teams").withNewValue(List.of(team3)));
change
@ -455,30 +485,38 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
.add(new FieldChange().withName("displayName").withOldValue("displayName").withNewValue("displayName1"));
change.getFieldsUpdated().add(new FieldChange().withName("profile").withOldValue(profile).withNewValue(profile1));
change.getFieldsUpdated().add(new FieldChange().withName("isBot").withOldValue(false).withNewValue(true));
user = patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change);
user = patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change, update);
//
// Remove the attributes
//
origJson = JsonUtils.pojoToJson(user);
user.withRoles(null)
user.withRoles(Arrays.asList(defaultRoleRef))
.withTeams(null)
.withTimezone(null)
.withDisplayName(null)
.withProfile(null)
.withIsBot(null)
.withIsAdmin(false);
update =
JsonUtils.readValue(origJson, User.class)
.withRoles(null)
.withTeams(null)
.withTimezone(null)
.withDisplayName(null)
.withProfile(null)
.withIsBot(null)
.withIsAdmin(false);
// Note non-empty display field is not deleted
change = getChangeDescription(user.getVersion());
change.getFieldsDeleted().add(new FieldChange().withName("roles").withOldValue(roles2));
change.getFieldsDeleted().add(new FieldChange().withName("roles").withOldValue(Arrays.asList(role2)));
change.getFieldsDeleted().add(new FieldChange().withName("teams").withOldValue(teams1));
change.getFieldsDeleted().add(new FieldChange().withName("timezone").withOldValue(timezone1));
change.getFieldsDeleted().add(new FieldChange().withName("displayName").withOldValue("displayName1"));
change.getFieldsDeleted().add(new FieldChange().withName("profile").withOldValue(profile1));
change.getFieldsDeleted().add(new FieldChange().withName("isBot").withOldValue(true).withNewValue(null));
patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change);
patchEntityAndCheck(user, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change, update);
}
@Test

View File

@ -7,5 +7,5 @@ Provides metadata version information.
from incremental import Version
__version__ = Version("metadata", 0, 9, 0, dev=10)
__version__ = Version("metadata", 0, 9, 0, dev=11)
__all__ = ["__version__"]