@Test
  public void createAndApprove() {
    Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));

    // self-create user with membership: goes 'createApproval' with resources and membership but no
    // propagation
    UserTO userTO = UserITCase.getUniqueSampleTO("*****@*****.**");
    userTO
        .getMemberships()
        .add(new MembershipTO.Builder().group("29f96485-729e-4d31-88a1-6fc60e4677f3").build());
    userTO.getResources().add(RESOURCE_NAME_TESTDB);

    SyncopeClient anonClient = clientFactory.create();
    userTO =
        anonClient
            .getService(UserSelfService.class)
            .create(userTO, true)
            .readEntity(new GenericType<ProvisioningResult<UserTO>>() {})
            .getEntity();
    assertNotNull(userTO);
    assertEquals("createApproval", userTO.getStatus());
    assertFalse(userTO.getMemberships().isEmpty());
    assertFalse(userTO.getResources().isEmpty());

    try {
      resourceService.readConnObject(
          RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
      fail();
    } catch (SyncopeClientException e) {
      assertEquals(ClientExceptionType.NotFound, e.getType());
    }

    // now approve and verify that propagation has happened
    WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
    form = userWorkflowService.claimForm(form.getTaskId());
    Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
    props.get("approve").setValue(Boolean.TRUE.toString());
    form.getProperties().clear();
    form.getProperties().addAll(props.values());
    userTO = userWorkflowService.submitForm(form);
    assertNotNull(userTO);
    assertEquals("active", userTO.getStatus());
    assertNotNull(
        resourceService.readConnObject(
            RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey()));
  }
  @Override
  public BulkActionResult bulkDeassociation(
      final String key,
      final String anyTypeKey,
      final ResourceDeassociationAction type,
      final List<AnyKey> keys) {

    AbstractResourceAssociator<? extends AnyTO> associator =
        anyTypeKey.equalsIgnoreCase(AnyTypeKind.USER.name())
            ? userLogic
            : anyTypeKey.equalsIgnoreCase(AnyTypeKind.GROUP.name()) ? groupLogic : anyObjectLogic;

    BulkActionResult result = new BulkActionResult();

    for (AnyKey anyKey : keys) {
      Set<String> resources = Collections.singleton(key);
      try {
        switch (type) {
          case DEPROVISION:
            associator.deprovision(anyKey.getElement(), resources);
            break;

          case UNASSIGN:
            associator.unassign(anyKey.getElement(), resources);
            break;

          case UNLINK:
            associator.unlink(anyKey.getElement(), resources);
            break;

          default:
        }

        result
            .getResults()
            .put(String.valueOf(anyKey.getElement()), BulkActionResult.Status.SUCCESS);
      } catch (Exception e) {
        LOG.warn("While executing {} on {} {}", type, anyTypeKey, anyKey.getElement(), e);
        result
            .getResults()
            .put(String.valueOf(anyKey.getElement()), BulkActionResult.Status.FAILURE);
      }
    }

    return result;
  }
  @Test
  public void updateWithApproval() {
    Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));

    // 1. create user as admin
    UserTO created =
        createUser(UserITCase.getUniqueSampleTO("*****@*****.**")).getEntity();
    assertNotNull(created);
    assertFalse(created.getUsername().endsWith("XX"));

    // 2. self-update (username + memberships + resource) - works but needs approval
    UserPatch userPatch = new UserPatch();
    userPatch.setKey(created.getKey());
    userPatch.setUsername(
        new StringReplacePatchItem.Builder().value(created.getUsername() + "XX").build());
    userPatch
        .getMemberships()
        .add(
            new MembershipPatch.Builder()
                .operation(PatchOperation.ADD_REPLACE)
                .group("bf825fe1-7320-4a54-bd64-143b5c18ab97")
                .build());
    userPatch
        .getResources()
        .add(
            new StringPatchItem.Builder()
                .operation(PatchOperation.ADD_REPLACE)
                .value(RESOURCE_NAME_TESTDB)
                .build());
    userPatch.setPassword(
        new PasswordPatch.Builder()
            .value("newPassword123")
            .onSyncope(false)
            .resource(RESOURCE_NAME_TESTDB)
            .build());

    SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
    UserTO updated =
        authClient
            .getService(UserSelfService.class)
            .update(userPatch)
            .readEntity(new GenericType<ProvisioningResult<UserTO>>() {})
            .getEntity();
    assertNotNull(updated);
    assertEquals("updateApproval", updated.getStatus());
    assertFalse(updated.getUsername().endsWith("XX"));
    assertTrue(updated.getMemberships().isEmpty());

    // no propagation happened
    assertTrue(updated.getResources().isEmpty());
    try {
      resourceService.readConnObject(
          RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey());
      fail();
    } catch (SyncopeClientException e) {
      assertEquals(ClientExceptionType.NotFound, e.getType());
    }

    // 3. approve self-update as admin
    WorkflowFormTO form = userWorkflowService.getFormForUser(updated.getKey());
    form = userWorkflowService.claimForm(form.getTaskId());
    Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
    props.get("approve").setValue(Boolean.TRUE.toString());
    form.getProperties().clear();
    form.getProperties().addAll(props.values());
    updated = userWorkflowService.submitForm(form);
    assertNotNull(updated);
    assertEquals("active", updated.getStatus());
    assertTrue(updated.getUsername().endsWith("XX"));
    assertEquals(1, updated.getMemberships().size());

    // check that propagation also happened
    assertTrue(updated.getResources().contains(RESOURCE_NAME_TESTDB));
    assertNotNull(
        resourceService.readConnObject(
            RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey()));
  }
  @Test
  public void issue42() {
    PlainSchema userId = plainSchemaDAO.find("userId");

    Set<MappingItem> beforeUserIdMappings = new HashSet<>();
    for (ExternalResource res : resourceDAO.findAll()) {
      if (res.getProvision(anyTypeDAO.findUser()) != null
          && res.getProvision(anyTypeDAO.findUser()).getMapping() != null) {

        for (MappingItem mapItem :
            res.getProvision(anyTypeDAO.findUser()).getMapping().getItems()) {
          if (userId.getKey().equals(mapItem.getIntAttrName())) {
            beforeUserIdMappings.add(mapItem);
          }
        }
      }
    }

    ResourceTO resourceTO = new ResourceTO();
    resourceTO.setKey("resource-issue42");
    resourceTO.setConnector(100L);
    resourceTO.setEnforceMandatoryCondition(true);

    ProvisionTO provisionTO = new ProvisionTO();
    provisionTO.setAnyType(AnyTypeKind.USER.name());
    provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME);
    resourceTO.getProvisions().add(provisionTO);

    MappingTO mapping = new MappingTO();
    provisionTO.setMapping(mapping);

    MappingItemTO item = new MappingItemTO();
    item.setIntAttrName("userId");
    item.setIntMappingType(IntMappingType.UserPlainSchema);
    item.setExtAttrName("campo1");
    item.setConnObjectKey(true);
    item.setMandatoryCondition("false");
    item.setPurpose(MappingPurpose.BOTH);
    mapping.setConnObjectKeyItem(item);

    ExternalResource resource = resourceDataBinder.create(resourceTO);
    resource = resourceDAO.save(resource);
    assertNotNull(resource);
    assertNotNull(resource.getProvision(anyTypeDAO.findUser()).getMapping());
    assertEquals(1, resource.getProvision(anyTypeDAO.findUser()).getMapping().getItems().size());

    resourceDAO.flush();

    ExternalResource actual = resourceDAO.find("resource-issue42");
    assertNotNull(actual);
    assertEquals(resource, actual);

    userId = plainSchemaDAO.find("userId");

    Set<MappingItem> afterUserIdMappings = new HashSet<>();
    for (ExternalResource res : resourceDAO.findAll()) {
      if (res.getProvision(anyTypeDAO.findUser()) != null
          && res.getProvision(anyTypeDAO.findUser()).getMapping() != null) {

        for (MappingItem mapItem :
            res.getProvision(anyTypeDAO.findUser()).getMapping().getItems()) {
          if (userId.getKey().equals(mapItem.getIntAttrName())) {
            afterUserIdMappings.add(mapItem);
          }
        }
      }
    }

    assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1);
  }