All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.captaingoldfish.scim.sdk.server.patch.DefaultPatchOperationHandler Maven / Gradle / Ivy

There is a newer version: 1.26.0
Show newest version
// Generated by delombok at Sat Aug 24 10:10:59 CEST 2024
package de.captaingoldfish.scim.sdk.server.patch;

import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import de.captaingoldfish.scim.sdk.common.constants.enums.HttpMethod;
import de.captaingoldfish.scim.sdk.common.exceptions.ResourceNotFoundException;
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
import de.captaingoldfish.scim.sdk.common.resources.ServiceProvider;
import de.captaingoldfish.scim.sdk.common.resources.complex.Meta;
import de.captaingoldfish.scim.sdk.common.resources.complex.PatchConfig;
import de.captaingoldfish.scim.sdk.common.schemas.SchemaAttribute;
import de.captaingoldfish.scim.sdk.common.utils.JsonHelper;
import de.captaingoldfish.scim.sdk.server.endpoints.Context;
import de.captaingoldfish.scim.sdk.server.endpoints.ResourceHandler;
import de.captaingoldfish.scim.sdk.server.endpoints.features.EndpointType;
import de.captaingoldfish.scim.sdk.server.endpoints.validation.RequestValidatorHandler;
import de.captaingoldfish.scim.sdk.server.patch.operations.MultivaluedComplexAttributeOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.MultivaluedComplexMultivaluedSubAttributeOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.MultivaluedComplexSimpleSubAttributeOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.MultivaluedSimpleAttributeOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.PatchOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.RemoveComplexAttributeOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.RemoveExtensionRefOperation;
import de.captaingoldfish.scim.sdk.server.patch.operations.SimpleAttributeOperation;
import de.captaingoldfish.scim.sdk.server.schemas.ResourceType;
import de.captaingoldfish.scim.sdk.server.schemas.validation.RequestResourceValidator;


/**
 * This class represents the default patch-operation-handler for all {@link ResourceHandler}s
 *
 * @author Pascal Knueppel
 * @since 06.01.2024
 */
public class DefaultPatchOperationHandler implements PatchOperationHandler
{

  @java.lang.SuppressWarnings("all")
  @lombok.Generated
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultPatchOperationHandler.class);

  /**
   * the {@link ResourceNode}-type that is represented by this handler
   */
  private final Class type;

  /**
   * the patch configuration of the service provider
   */
  private final PatchConfig patchConfig;

  /**
   * the endpoint-definition for the resource
   */
  private final ResourceType resourceType;

  /**
   * the current request-context
   */
  private final Context context;

  /**
   * the resource is retrieved from {@link ResourceHandler#getResource(String, List, List, Context)} and all
   * patch-operations will be applied to this object.
   */
  private T patchedResource;

  /**
   * a copy of the original resource that is being set in the method
   * {@link #getOldResourceSupplier(String, List, List, Context)}
   */
  private T oldResource;

  public DefaultPatchOperationHandler(Class type,
                                      PatchConfig patchConfig,
                                      ResourceType resourceType,
                                      Context context)
  {
    this.type = type;
    this.patchConfig = patchConfig;
    this.resourceType = resourceType;
    this.context = context;
  }

  /**
   * this supplier simply calls the get-method of the {@link ResourceHandler} and stores the result twice. One
   * copy is used to apply the patch operations and the other copy is used to provide the original state of the
   * resource if needed
   *
   * @param id the id of the resource
   * @param attributes optional request-attributes
   * @param excludedAttributes optional attributes to exclude from the resource
   */
  @Override
  public Supplier getOldResourceSupplier(String id,
                                            List attributes,
                                            List excludedAttributes,
                                            Context context)
  {
    return () -> {
      if (patchedResource == null)
      {
        patchedResource = (T)resourceType.getResourceHandlerImpl()
                                         .getResourceForUpdate(id,
                                                               attributes,
                                                               excludedAttributes,
                                                               context,
                                                               EndpointType.PATCH);
        // TODO enhance this creepy copy creation
        oldResource = JsonHelper.readJsonDocument(patchedResource.toString(), type);
      }
      return oldResource;
    };
  }

  /**
   * this method handles all types of patch-operations by giving each patch-operation to the
   * {@link PatchTargetHandler}
   *
   * @param id the id of the resource that is being patched
   * @param patchOperation the patch-operation that should be applied to the resource
   * @return true if an effective change was applied by the given patch-operation
   */
  private boolean handlePatchOperation(String id, PatchOperation patchOperation)
  {
    PatchTargetHandler patchTargetHandler = new PatchTargetHandler(patchConfig, resourceType, patchOperation);
    T patchedResourceNode = getPatchedResource(id);
    List values = (List)patchOperation.getValueStringList()
                                                      .stream()
                                                      .map(object -> String.valueOf(object))
                                                      .collect(Collectors.toList());
    if (log.isTraceEnabled())
    {
      log.trace("handling \'{}\'-operation of type \'{}\' with values \'{}\'",
                patchOperation.getPatchOp(),
                patchOperation.getClass().getSimpleName(),
                values);
    }
    return patchTargetHandler.handleOperationValues(patchedResourceNode, values);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, RemoveExtensionRefOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, SimpleAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, MultivaluedSimpleAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, RemoveComplexAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, MultivaluedComplexAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, MultivaluedComplexMultivaluedSubAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean handleOperation(String id, MultivaluedComplexSimpleSubAttributeOperation patchOperation)
  {
    return handlePatchOperation(id, patchOperation);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public T getPatchedResource(String id)
  {
    if (patchedResource == null)
    {
      patchedResource = (T)resourceType.getResourceHandlerImpl()
                                       .getResource(id, Collections.emptyList(), Collections.emptyList(), context);
      if (patchedResource == null)
      {
        throw new ResourceNotFoundException("the \'" + resourceType.getName() + "\' resource with id \'" + id
                                            + "\' does " + "not exist", null, null);
      }
      // TODO enhance this creepy copy creation
      oldResource = JsonHelper.readJsonDocument(patchedResource.toString(), type);
    }
    return patchedResource;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public T getUpdatedResource(String resourceId,
                              T patchedResource,
                              boolean wasResourceChanged,
                              List attributes,
                              List excludedAttributes,
                              Context context)
  {
    if (wasResourceChanged)
    {
      T validatedResource;
      ServiceProvider serviceProvider = resourceType.getResourceHandlerImpl().getServiceProvider();
      RequestResourceValidator requestResourceValidator = new RequestResourceValidator(serviceProvider, resourceType,
                                                                                       HttpMethod.PATCH);
      validatedResource = (T)requestResourceValidator.validateDocument(patchedResource);
      validatedResource.setId(resourceId);
      Supplier oldResourceSupplier = getOldResourceSupplier(resourceId,
                                                               Collections.emptyList(),
                                                               Collections.emptyList(),
                                                               context);
      // handle meta attribute
      {
        final Meta meta = validatedResource.getMeta().orElseGet(() -> {
          Meta newMeta = new Meta();
          validatedResource.setMeta(newMeta);
          return newMeta;
        });
        final String location = context.getResourceReferenceUrl(resourceId);
        meta.setLocation(location);
        meta.setResourceType(resourceType.getName());
        if (!meta.getCreated().isPresent())
        {
          meta.setCreated(meta.getLastModified().orElseGet(Instant::now));
        }
        if (!meta.getLastModified().isPresent())
        {
          meta.setLastModified(meta.getCreated().orElse(null));
        }
      }
      new RequestValidatorHandler(resourceType.getResourceHandlerImpl(), requestResourceValidator,
                                  context).validateUpdate((Supplier)oldResourceSupplier,
                                                          validatedResource);
      validatedResource.setId(resourceId); // this will make sure that the id is not lost after resource-validation
      return (T)resourceType.getResourceHandlerImpl().updateResource(patchedResource, context);
    }
    else
    {
      return patchedResource;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy