Skip to content

Commit

Permalink
[eclipse-ditto#926] add command and event strategies for ActivateSubj…
Browse files Browse the repository at this point in the history
…ects.

Signed-off-by: Yufei Cai <yufei.cai@bosch.io>
  • Loading branch information
yufei-cai committed Dec 24, 2020
1 parent c9d1b91 commit 23d261f
Show file tree
Hide file tree
Showing 11 changed files with 567 additions and 17 deletions.
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.model.policies;

import java.net.URI;
import java.text.MessageFormat;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;

import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.model.base.common.HttpStatusCode;
import org.eclipse.ditto.model.base.exceptions.DittoRuntimeException;
import org.eclipse.ditto.model.base.exceptions.DittoRuntimeExceptionBuilder;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.base.json.JsonParsableException;
import org.eclipse.ditto.model.base.json.JsonSchemaVersion;

/**
* Thrown if an action on a policy failed.
*/
@Immutable
@JsonParsableException(errorCode = PolicyActionFailedException.ERROR_CODE)
public final class PolicyActionFailedException extends DittoRuntimeException implements PolicyException {

/**
* Error code of this exception.
*/
public static final String ERROR_CODE = ERROR_CODE_PREFIX + "action.failed";

private static final HttpStatusCode DEFAULT_STATUS = HttpStatusCode.INTERNAL_SERVER_ERROR;

private static final String DEFAULT_ACTION = "activateTokenIntegration";

private static final String MESSAGE_TEMPLATE = "Failed to execute action ''{0}''.";

private static final String DEFAULT_DESCRIPTION = "Please contact the service team.";

private PolicyActionFailedException(final DittoHeaders dittoHeaders,
final HttpStatusCode status,
@Nullable final String message,
@Nullable final String description,
@Nullable final Throwable cause,
@Nullable final URI href) {
super(ERROR_CODE, status, dittoHeaders, message, description, cause, href);
}

/**
* A mutable builder for a {@code PolicyActionFailedException}.
*
* @return the builder.
*/
public static Builder newBuilder() {
return new Builder();
}

/**
* Constructs a new {@code PolicyActionFailedException} object with the exception content extracted from the
* given JSON object.
*
* @param jsonObject the JSON to read the exception content from.
* @param dittoHeaders the headers of the command which resulted in this exception.
* @return the new PolicyActionFailedException.
* @throws NullPointerException if any argument is {@code null}.
* @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
* @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
* format.
*/
public static PolicyActionFailedException fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
}

@Override
public JsonSchemaVersion[] getSupportedSchemaVersions() {
return new JsonSchemaVersion[]{JsonSchemaVersion.V_2};
}

@Override
public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
return new Builder()
.message(getMessage())
.description(getDescription().orElse(null))
.cause(getCause())
.href(getHref().orElse(null))
.dittoHeaders(dittoHeaders)
.build();
}

/**
* A mutable builder with a fluent API for a {@link PolicyActionFailedException}.
*/
@NotThreadSafe
public static final class Builder extends DittoRuntimeExceptionBuilder<PolicyActionFailedException> {

private HttpStatusCode status = DEFAULT_STATUS;

private Builder() {
description(DEFAULT_DESCRIPTION);
action(DEFAULT_ACTION);
}

/**
* Set the error message to mention the failed action.
*
* @param action the failed action.
* @return this builder.
*/
public DittoRuntimeExceptionBuilder<PolicyActionFailedException> action(final String action) {
return message(MessageFormat.format(MESSAGE_TEMPLATE, action));
}

/**
* Set the status code.
*
* @param status the status code.
* @return this builder.
*/
public DittoRuntimeExceptionBuilder<PolicyActionFailedException> status(final HttpStatusCode status) {
this.status = status;
return this;
}

@Override
protected PolicyActionFailedException doBuild(final DittoHeaders dittoHeaders,
@Nullable final String message,
@Nullable final String description,
@Nullable final Throwable cause,
@Nullable final URI href) {
return new PolicyActionFailedException(dittoHeaders, status, message, description, cause, href);
}

}

}
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.ditto.model.base.headers.entitytag.EntityTag;
import org.eclipse.ditto.model.policies.Label;
import org.eclipse.ditto.model.policies.Policy;
import org.eclipse.ditto.model.policies.PolicyActionFailedException;
import org.eclipse.ditto.model.policies.PolicyEntry;
import org.eclipse.ditto.model.policies.PolicyId;
import org.eclipse.ditto.model.policies.Subject;
Expand Down Expand Up @@ -92,7 +93,8 @@ protected Result<PolicyEvent> doApply(final Context<PolicyId> context,
command);
}
} else {
return ResultFactory.newErrorResult(policyEntryNotFound(policyId, label, dittoHeaders), command);
// Command is constructed incorrectly. This is a bug.
return ResultFactory.newErrorResult(PolicyActionFailedException.newBuilder().build(), command);
}
}

Expand Down
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.services.policies.persistence.actors.strategies.commands;

import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import org.eclipse.ditto.model.base.entity.metadata.Metadata;
import org.eclipse.ditto.model.base.exceptions.DittoRuntimeException;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.base.headers.entitytag.EntityTag;
import org.eclipse.ditto.model.policies.Label;
import org.eclipse.ditto.model.policies.Policy;
import org.eclipse.ditto.model.policies.PolicyActionFailedException;
import org.eclipse.ditto.model.policies.PolicyBuilder;
import org.eclipse.ditto.model.policies.PolicyEntry;
import org.eclipse.ditto.model.policies.PolicyId;
import org.eclipse.ditto.model.policies.Subject;
import org.eclipse.ditto.model.policies.SubjectExpiry;
import org.eclipse.ditto.model.policies.SubjectId;
import org.eclipse.ditto.services.models.policies.PoliciesValidator;
import org.eclipse.ditto.services.policies.common.config.PolicyConfig;
import org.eclipse.ditto.services.policies.persistence.actors.placeholders.PolicyEntryPlaceholder;
import org.eclipse.ditto.services.utils.persistentactors.results.Result;
import org.eclipse.ditto.services.utils.persistentactors.results.ResultFactory;
import org.eclipse.ditto.signals.commands.policies.modify.ActivateSubjects;
import org.eclipse.ditto.signals.commands.policies.modify.ActivateSubjectsResponse;
import org.eclipse.ditto.signals.events.policies.PolicyEvent;
import org.eclipse.ditto.signals.events.policies.SubjectsActivated;

/**
* This strategy handles the {@link org.eclipse.ditto.signals.commands.policies.modify.ActivateSubjects} command.
*/
final class ActivateSubjectsStrategy extends AbstractPolicyCommandStrategy<ActivateSubjects> {

ActivateSubjectsStrategy(final PolicyConfig policyConfig) {
super(ActivateSubjects.class, policyConfig);
}

@Override
protected Result<PolicyEvent> doApply(final Context<PolicyId> context,
@Nullable final Policy policy,
final long nextRevision,
final ActivateSubjects command,
@Nullable final Metadata metadata) {

final Policy nonNullPolicy = checkNotNull(policy, "policy");
final PolicyId policyId = context.getState();
final SubjectExpiry commandSubjectExpiry = SubjectExpiry.newInstance(command.getExpiry());
final DittoHeaders dittoHeaders = command.getDittoHeaders();
final List<PolicyEntry> entries = command.getLabels()
.stream()
.map(nonNullPolicy::getEntryFor)
.flatMap(Optional::stream)
.collect(Collectors.toList());
if (entries.isEmpty() || entries.size() != command.getLabels().size()) {
// Command is constructed incorrectly. This is a bug.
return ResultFactory.newErrorResult(PolicyActionFailedException.newBuilder().build(), command);
}
final PolicyBuilder policyBuilder = nonNullPolicy.toBuilder();
final Map<Label, Subject> activatedSubjects = new HashMap<>();
for (final PolicyEntry entry : entries) {
final SubjectId subjectId;
try {
subjectId = PolicyEntryPlaceholder.resolveSubjectId(entry, command.getSubjectId());
} catch (final DittoRuntimeException e) {
return ResultFactory.newErrorResult(e, command);
}
final Subject subject = Subject.newInstance(subjectId, TOKEN_INTEGRATION, commandSubjectExpiry);
final Subject adjustedSubject = potentiallyAdjustSubject(subject);
policyBuilder.setSubjectFor(entry.getLabel(), adjustedSubject);
activatedSubjects.put(entry.getLabel(), adjustedSubject);
}

// Validation is necessary because activation may add expiry to the policy admin subject.
final Policy newPolicy = policyBuilder.build();
final PoliciesValidator validator = PoliciesValidator.newInstance(newPolicy);
if (validator.isValid()) {
final PolicyEvent<?> event =
SubjectsActivated.of(policyId, activatedSubjects, nextRevision, getEventTimestamp(), dittoHeaders);
final ActivateSubjectsResponse rawResponse =
ActivateSubjectsResponse.of(policyId, command.getSubjectId(), dittoHeaders);
// do not append ETag - activated subjects do not support ETags.
return ResultFactory.newMutationResult(command, event, rawResponse);
} else {
return ResultFactory.newErrorResult(
policyInvalid(policyId, validator.getReason().orElse(null), dittoHeaders),
command);
}
}

@Override
public Optional<EntityTag> previousEntityTag(final ActivateSubjects command,
@Nullable final Policy previousEntity) {
// activated subjects do not support entity tag
return Optional.empty();
}

@Override
public Optional<EntityTag> nextEntityTag(final ActivateSubjects command, @Nullable final Policy newEntity) {
// activated subjects do not support entity tag
return Optional.empty();
}
}
Expand Up @@ -42,6 +42,7 @@ private PolicyCommandStrategies(final PolicyConfig policyConfig) {
addStrategy(new ModifyPolicyStrategy(policyConfig));
addStrategy(new RetrievePolicyStrategy(policyConfig));
addStrategy(new DeletePolicyStrategy(policyConfig));
addStrategy(new ActivateSubjectsStrategy(policyConfig));

// Policy Entries
addStrategy(new ModifyPolicyEntriesStrategy(policyConfig));
Expand Down
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.ditto.signals.events.policies.SubjectCreated;
import org.eclipse.ditto.signals.events.policies.SubjectDeleted;
import org.eclipse.ditto.signals.events.policies.SubjectModified;
import org.eclipse.ditto.signals.events.policies.SubjectsActivated;
import org.eclipse.ditto.signals.events.policies.SubjectsModified;

/**
Expand All @@ -48,6 +49,7 @@ private PolicyEventStrategies() {
addStrategy(PolicyEntryModified.class, new PolicyEntryModifiedStrategy());
addStrategy(PolicyEntryDeleted.class, new PolicyEntryDeletedStrategy());
addStrategy(SubjectsModified.class, new SubjectsModifiedStrategy());
addStrategy(SubjectsActivated.class, new SubjectsActivatedStrategy());
addStrategy(SubjectCreated.class, new SubjectCreatedStrategy());
addStrategy(SubjectModified.class, new SubjectModifiedStrategy());
addStrategy(SubjectDeleted.class, new SubjectDeletedStrategy());
Expand Down
Expand Up @@ -21,7 +21,7 @@
final class SubjectActivatedStrategy extends AbstractPolicyEventStrategy<SubjectActivated> {

@Override
protected PolicyBuilder applyEvent(final SubjectActivated sm, final PolicyBuilder policyBuilder) {
return policyBuilder.setSubjectFor(sm.getLabel(), sm.getSubject());
protected PolicyBuilder applyEvent(final SubjectActivated event, final PolicyBuilder policyBuilder) {
return policyBuilder.setSubjectFor(event.getLabel(), event.getSubject());
}
}
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.services.policies.persistence.actors.strategies.events;

import org.eclipse.ditto.model.policies.PolicyBuilder;
import org.eclipse.ditto.signals.events.policies.SubjectsActivated;

/**
* This strategy handles {@link org.eclipse.ditto.signals.events.policies.SubjectsActivated} events.
*/
final class SubjectsActivatedStrategy extends AbstractPolicyEventStrategy<SubjectsActivated> {

@Override
protected PolicyBuilder applyEvent(final SubjectsActivated event, final PolicyBuilder policyBuilder) {
event.getActivatedSubjects().forEach(policyBuilder::setSubjectFor);
return policyBuilder;
}
}

0 comments on commit 23d261f

Please sign in to comment.