This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
commit 02e8a2c14c389f302ec811c84756af33315c639b Author: Andi Huber <[email protected]> AuthorDate: Sat Apr 25 21:25:19 2020 +0200 ISIS-2340: share logic of ObjectAdapterAccessHelper/UpdateHelper (8) --- .../metamodel/spec/interaction/ActionHandle.java | 44 +++ .../spec/interaction/CollectionHandle.java | 45 +++ .../spec/interaction/InteractionVeto.java | 50 +-- .../metamodel/spec/interaction/ManagedAction.java | 83 +++++ .../spec/interaction/ManagedCollection.java | 90 +++++ .../metamodel/spec/interaction/ManagedMember.java | 130 +++++++ .../spec/interaction/ManagedProperty.java | 109 ++++++ .../metamodel/spec/interaction/MemberHandle.java | 81 ++++ .../metamodel/spec/interaction/PropertyHandle.java | 48 +++ .../viewer/vaadin/ui/binding/BinderUtil.java | 6 +- .../vaadin/ui/components/collection/TableView.java | 14 +- .../ui/components/object/ObjectFormView.java | 59 +-- .../common/model/binding/UiComponentFactory.java | 17 +- .../binding/interaction/ActionAccessChain.java | 53 --- .../model/binding/interaction/ActionBinding.java | 25 -- .../binding/interaction/ActionBindingReadonly.java | 19 - .../binding/interaction/ActionBindingUsable.java | 19 - .../binding/interaction/CollectionAccessChain.java | 54 --- .../binding/interaction/CollectionBinding.java | 25 -- .../interaction/CollectionBindingEditable.java | 19 - .../interaction/CollectionBindingReadonly.java | 19 - .../model/binding/interaction/ObjectBinding.java | 406 +++------------------ .../binding/interaction/PropertyAccessChain.java | 56 --- .../model/binding/interaction/PropertyBinding.java | 31 -- .../interaction/PropertyBindingEditable.java | 37 -- .../interaction/PropertyBindingReadonly.java | 27 -- .../resources/DomainObjectResourceServerside.java | 30 +- .../viewer/resources/DomainResourceHelper.java | 14 +- .../resources/InteractionFailureHandler.java | 56 ++- .../resources/ObjectAdapterAccessHelper.java | 71 +++- 30 files changed, 875 insertions(+), 862 deletions(-) diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java new file mode 100644 index 0000000..7f71884 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import org.apache.isis.core.commons.internal.base._Either; + +import lombok.NonNull; + +public final class ActionHandle extends MemberHandle<ManagedAction, ActionHandle> { + + ActionHandle(@NonNull _Either<ManagedAction, InteractionVeto> chain) { + super(chain); + } + +// public PropertyHandle modifyProperty( +// @NonNull final Function<ManagedProperty, ManagedObject> newProperyValueProvider) { +// +// chain = chain.leftRemap(property->{ +// val validityVeto = property.modifyProperty(newProperyValueProvider.apply(property)); +// return validityVeto.isPresent() +// ? _Either.right(validityVeto.get()) +// : _Either.left(property); +// }); +// return this; +// } + + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java new file mode 100644 index 0000000..c08f041 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import org.apache.isis.core.commons.internal.base._Either; + +import lombok.NonNull; + +public final class CollectionHandle extends MemberHandle<ManagedCollection, CollectionHandle> { + + CollectionHandle(@NonNull _Either<ManagedCollection, InteractionVeto> chain) { + super(chain); + } + +// public PropertyHandle modifyProperty( +// @NonNull final Function<ManagedProperty, ManagedObject> newProperyValueProvider) { +// +// chain = chain.leftRemap(property->{ +// val validityVeto = property.modifyProperty(newProperyValueProvider.apply(property)); +// return validityVeto.isPresent() +// ? _Either.right(validityVeto.get()) +// : _Either.left(property); +// }); +// return this; +// } + + +} + diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java similarity index 50% rename from viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java index feee940..61e4e88 100644 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java @@ -16,49 +16,55 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.isis.viewer.common.model.binding.interaction; +package org.apache.isis.core.metamodel.spec.interaction; + +import java.io.Serializable; + +import org.apache.isis.core.metamodel.consent.Consent; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; +@Getter @RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE) -public class InteractionResponse { - - public static enum Veto { +public class InteractionVeto implements Serializable { + + private static final long serialVersionUID = 1L; + + public static enum VetoType { NOT_FOUND, HIDDEN, - FORBIDDEN, - UNAUTHORIZED, + READONLY, + INVALID, } - private final static InteractionResponse SUCCESS = of(null, null); - @Getter private final Veto veto; - @Getter private final String failureMessage; + @NonNull private final VetoType vetoType; + @NonNull private final Consent vetoConsent; - public boolean isSuccess() { - return veto==null; + public static InteractionVeto notFound(@NonNull Consent vetoConsent) { + return of(VetoType.NOT_FOUND, vetoConsent); } - public boolean isFailure() { - return !isSuccess(); + public static InteractionVeto hidden(@NonNull Consent vetoConsent) { + return of(VetoType.HIDDEN, vetoConsent); } - // -- FACTORIES - - public static InteractionResponse failed(@NonNull Veto veto) { - return of(veto, "unspecified"); + public static InteractionVeto readonly(@NonNull Consent vetoConsent) { + return of(VetoType.READONLY, vetoConsent); } - public static InteractionResponse failed(@NonNull Veto veto, String reason) { - return of(veto, reason); + public static InteractionVeto invalid(@NonNull Consent vetoConsent) { + return of(VetoType.INVALID, vetoConsent); } - public static InteractionResponse success() { - return SUCCESS; + public String getReason() { + return getVetoConsent().getReason(); } - + public String getDescription() { + return getVetoConsent().getDescription(); + } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java new file mode 100644 index 0000000..2e88681 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.Optional; + +import org.apache.isis.core.commons.internal.base._Either; +import org.apache.isis.core.metamodel.consent.Veto; +import org.apache.isis.core.metamodel.spec.ManagedObject; +import org.apache.isis.core.metamodel.spec.feature.ObjectAction; + +import lombok.Getter; +import lombok.NonNull; +import lombok.val; + +public final class ManagedAction extends ManagedMember { + + // -- FACTORIES + + public static final Optional<ManagedAction> lookupAction( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + return ManagedMember.<ObjectAction>lookup(owner, MemberType.ACTION, memberId) + .map(objectAction -> new ManagedAction(owner, objectAction)); + } + + public static final ActionHandle getActionHandle( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + val managedAction = ManagedAction.lookupAction(owner, memberId); + + final _Either<ManagedAction, InteractionVeto> chain = managedAction.isPresent() + ? _Either.left(managedAction.get()) + : _Either.right(InteractionVeto.notFound(new Veto(notFound(MemberType.ACTION, memberId)))); + + return new ActionHandle(chain); + } + + // -- IMPLEMENTATION + + @Getter private final ObjectAction action; + + private ManagedAction( + final @NonNull ManagedObject owner, + final @NonNull ObjectAction action) { + + super(owner); + this.action = action; + } + + @Override + public ObjectAction getMember() { + return getAction(); + } + + @Override + public MemberType getMemberType() { + return MemberType.ACTION; + } + + public String getName() { + return getAction().getName(); + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java new file mode 100644 index 0000000..e51b322 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.Optional; + +import org.apache.isis.core.commons.internal.base._Either; +import org.apache.isis.core.metamodel.consent.Veto; +import org.apache.isis.core.metamodel.spec.ManagedObject; +import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; + +import lombok.Getter; +import lombok.NonNull; +import lombok.val; + +public final class ManagedCollection extends ManagedMember { + + // -- FACTORIES + + public static final Optional<ManagedCollection> lookupCollection( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + return ManagedMember.<OneToManyAssociation>lookup(owner, MemberType.COLLECTION, memberId) + .map(objectAction -> new ManagedCollection(owner, objectAction)); + } + + public static final CollectionHandle getCollectionHandle( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + val managedCollection = ManagedCollection.lookupCollection(owner, memberId); + + final _Either<ManagedCollection, InteractionVeto> chain = managedCollection.isPresent() + ? _Either.left(managedCollection.get()) + : _Either.right(InteractionVeto.notFound(new Veto(notFound(MemberType.COLLECTION, memberId)))); + + return new CollectionHandle(chain); + } + + // -- IMPLEMENTATION + + @Getter private final OneToManyAssociation collection; + + private ManagedCollection( + final @NonNull ManagedObject owner, + final @NonNull OneToManyAssociation collection) { + + super(owner); + this.collection = collection; + } + + @Override + public OneToManyAssociation getMember() { + return getCollection(); + } + + @Override + public MemberType getMemberType() { + return MemberType.COLLECTION; + } + + public String getName() { + return getCollection().getName(); + } + + public ManagedObject getCollectionValue() { + val collection = getCollection(); + + return Optional.ofNullable(collection.get(getOwner())) + .orElse(ManagedObject.of(collection.getSpecification(), null)); + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java new file mode 100644 index 0000000..f9dc991 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.Optional; +import java.util.function.BiFunction; + +import org.apache.isis.applib.annotation.Where; +import org.apache.isis.core.commons.internal.base._Casts; +import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; +import org.apache.isis.core.metamodel.spec.ManagedObject; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.spec.feature.ObjectAction; +import org.apache.isis.core.metamodel.spec.feature.ObjectMember; +import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; +import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +@RequiredArgsConstructor +public abstract class ManagedMember { + + // only used to create failure messages + @RequiredArgsConstructor + public static enum MemberType { + PROPERTY(OneToOneAssociation.class, (spec, propertyId)-> + spec.getAssociation(propertyId) + .map(property->property.isOneToOneAssociation()?property:null)), + + COLLECTION(OneToManyAssociation.class, (spec, collectionId)-> + spec.getAssociation(collectionId) + .map(collection->collection.isOneToManyAssociation()?collection:null)), + + ACTION(ObjectAction.class, (spec, actionId)-> + spec.getObjectAction(actionId)); + + @Getter private final Class<? extends ObjectMember> memberType; + private final BiFunction< + ObjectSpecification, String, + Optional<? extends ObjectMember> + > memberProvider; + + public <T extends ObjectMember> Optional<T> lookup( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + val onwerSpec = owner.getSpecification(); + val member = memberProvider.apply(onwerSpec, memberId); + return _Casts.uncheckedCast(member); + } + + } + + @Getter + private final ManagedObject owner; + + public abstract ObjectMember getMember(); + + public abstract MemberType getMemberType(); + + public ObjectSpecification getSpecification() { + return getMember().getSpecification(); + } + + /** + * @param where + * @return non-empty if hidden + */ + public Optional<InteractionVeto> checkVisibility( + @NonNull final Where where) { + + val visibilityConsent = + getMember() + .isVisible(getOwner(), InteractionInitiatedBy.USER, where); + + return visibilityConsent.isVetoed() + ? Optional.of(InteractionVeto.hidden(visibilityConsent)) + : Optional.empty(); + } + + /** + * @param where + * @return non-empty if not usable/editable (meaning if read-only) + */ + public Optional<InteractionVeto> checkUsability( + @NonNull final Where where) { + + val usabilityConsent = + getMember() + .isUsable(getOwner(), InteractionInitiatedBy.USER, where); + + return usabilityConsent.isVetoed() + ? Optional.of(InteractionVeto.readonly(usabilityConsent)) + : Optional.empty(); + } + + protected static <T extends ObjectMember> Optional<T> lookup( + @NonNull final ManagedObject owner, + @NonNull final MemberType memberType, + @NonNull final String memberId) { + return memberType.lookup(owner, memberId); + } + + protected static <T extends ObjectMember> String notFound( + MemberType memberType, + String memberId) { + return String.format("%s '%s' either does not exist, is disabled or is not visible", + memberId, + memberType.name().toLowerCase()); + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java new file mode 100644 index 0000000..6471f4b --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.Optional; + +import javax.annotation.Nullable; + +import org.apache.isis.core.commons.internal.base._Either; +import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; +import org.apache.isis.core.metamodel.consent.Veto; +import org.apache.isis.core.metamodel.spec.ManagedObject; +import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; + +import lombok.Getter; +import lombok.NonNull; +import lombok.val; + +public final class ManagedProperty extends ManagedMember { + + // -- FACTORIES + + public static final Optional<ManagedProperty> lookupProperty( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + return ManagedMember.<OneToOneAssociation>lookup(owner, MemberType.PROPERTY, memberId) + .map(objectAction -> new ManagedProperty(owner, objectAction)); + } + + public static final PropertyHandle getPropertyHandle( + @NonNull final ManagedObject owner, + @NonNull final String memberId) { + + val managedProperty = ManagedProperty.lookupProperty(owner, memberId); + + final _Either<ManagedProperty, InteractionVeto> chain = managedProperty.isPresent() + ? _Either.left(managedProperty.get()) + : _Either.right(InteractionVeto.notFound(new Veto(notFound(MemberType.PROPERTY, memberId)))); + + return new PropertyHandle(chain); + } + + // -- IMPLEMENTATION + + @Getter private final OneToOneAssociation property; + + private ManagedProperty( + final @NonNull ManagedObject owner, + final @NonNull OneToOneAssociation property) { + + super(owner); + this.property = property; + } + + @Override + public OneToOneAssociation getMember() { + return getProperty(); + } + + @Override + public MemberType getMemberType() { + return MemberType.PROPERTY; + } + + // -- INTERACTION + + /** + * @param proposedNewValue + * @return non-empty if the interaction is not valid for given {@code proposedNewValue} + */ + public Optional<InteractionVeto> modifyProperty(@Nullable ManagedObject proposedNewValue) { + + val validityVeto = property.isAssociationValid(getOwner(), proposedNewValue, InteractionInitiatedBy.USER); + if (validityVeto.isVetoed()) { + return Optional.of(InteractionVeto.invalid(validityVeto)); + } + + property.set(getOwner(), proposedNewValue, InteractionInitiatedBy.USER); + return Optional.empty(); + } + + public ManagedObject getPropertyValue() { + val property = getProperty(); + + return Optional.ofNullable(property.get(getOwner())) + .orElse(ManagedObject.of(property.getSpecification(), null)); + } + + + + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java new file mode 100644 index 0000000..c3d6594 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.apache.isis.applib.annotation.Where; +import org.apache.isis.core.commons.internal.base._Casts; +import org.apache.isis.core.commons.internal.base._Either; + +import lombok.NonNull; +import lombok.val; + +public abstract class MemberHandle<T extends ManagedMember, H extends MemberHandle<T, ?>> { + + @NonNull protected _Either<T, InteractionVeto> chain; + + protected MemberHandle(@NonNull _Either<T, InteractionVeto> chain) { + this.chain = chain; + } + + public H checkVisibility(@NonNull final Where where) { + chain = chain.leftRemap(property->{ + val visibilityVeto = property.checkVisibility(where); + return visibilityVeto.isPresent() + ? _Either.right(visibilityVeto.get()) + : _Either.left(property); + }); + return _Casts.uncheckedCast(this); + } + + public H checkUsability(@NonNull final Where where) { + chain = chain.leftRemap(property->{ + val usablitiyVeto = property.checkUsability(where); + return usablitiyVeto.isPresent() + ? _Either.right(usablitiyVeto.get()) + : _Either.left(property); + }); + return _Casts.uncheckedCast(this); + } + + @Deprecated + public H peekOnFailure(Consumer<InteractionVeto> onFailure) { + chain.right().ifPresent(onFailure); + return _Casts.uncheckedCast(this); + } + + public <X extends Throwable> + T getOrElseThrow(Function<InteractionVeto, ? extends X> onFailure) throws X { + val value = chain.leftIfAny(); + if (value != null) { + return value; + } else { + throw onFailure.apply(chain.rightIfAny()); + } + } + + public Optional<T> get() { + return chain.left(); + } + + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java new file mode 100644 index 0000000..c77e1e1 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.core.metamodel.spec.interaction; + +import java.util.function.Function; + +import org.apache.isis.core.commons.internal.base._Either; +import org.apache.isis.core.metamodel.spec.ManagedObject; + +import lombok.NonNull; +import lombok.val; + +public final class PropertyHandle extends MemberHandle<ManagedProperty, PropertyHandle> { + + PropertyHandle(@NonNull _Either<ManagedProperty, InteractionVeto> chain) { + super(chain); + } + + public PropertyHandle modifyProperty( + @NonNull final Function<ManagedProperty, ManagedObject> newProperyValueProvider) { + + chain = chain.leftRemap(property->{ + val validityVeto = property.modifyProperty(newProperyValueProvider.apply(property)); + return validityVeto.isPresent() + ? _Either.right(validityVeto.get()) + : _Either.left(property); + }); + return this; + } + + +} diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java index 6b458c5..b90b1f6 100644 --- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java +++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java @@ -30,6 +30,7 @@ import com.vaadin.flow.data.converter.Converter; import com.vaadin.flow.data.converter.DateToSqlDateConverter; import com.vaadin.flow.data.converter.LocalDateToDateConverter; +import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto; import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request; import lombok.RequiredArgsConstructor; @@ -109,7 +110,10 @@ public final class BinderUtil { @Override public Result<P> convertToModel(P newValue, ValueContext context) { // propagate new value down the domain model, and handle validation feedback - val validationMessage = request.getPropagator(pojoType).apply(newValue); + val validationMessage = request.getPropagator(pojoType).apply(newValue) + .map(InteractionVeto::getReason) + .orElse(null); + return validationMessage==null ? Result.ok(newValue) : Result.error(validationMessage); diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java index 99135b0..be05900 100644 --- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java +++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java @@ -25,6 +25,7 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.orderedlayout.VerticalLayout; @@ -34,6 +35,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.Contributed; import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -70,14 +72,12 @@ public class TableView extends VerticalLayout { } /** - * Constructs a (page-able) {@link Grid} from given {@code objectAssociation} and {@code assocObject} - * @param objectAssociation - * @param assocObject + * Constructs a (page-able) {@link Grid} from given {@code managedCollection} + * @param managedCollection */ - public static TableView fromObjectAssociation( - @NonNull final ObjectAssociation objectAssociation, - @Nullable final ManagedObject assocObject) { + public static Component fromManagedCollection(ManagedCollection managedCollection) { + val assocObject = managedCollection.getOwner(); val assocObjectSpecification = assocObject.getSpecification(); val collectionFacet = assocObjectSpecification.getFacet(CollectionFacet.class); @@ -158,4 +158,6 @@ public class TableView extends VerticalLayout { return Optional.ofNullable(e.getMessage()).orElse(e.getClass().getName()); } } + + } diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java index d531fbb..3e6b8d0 100644 --- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java +++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java @@ -19,7 +19,6 @@ package org.apache.isis.incubator.viewer.vaadin.ui.components.object; import java.util.Collection; -import java.util.Optional; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasComponents; @@ -48,7 +47,9 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row; import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab; import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup; import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.core.metamodel.spec.interaction.ManagedAction; +import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection; +import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty; import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa; import org.apache.isis.incubator.viewer.vaadin.ui.components.collection.TableView; import org.apache.isis.viewer.common.model.binding.UiComponentFactory; @@ -170,38 +171,51 @@ public class ObjectFormView extends VerticalLayout { @Override protected void onAction(HasComponents container, ActionLayoutData actionData) { - objectInteractor.lookupAction(actionData.getId()) - .ifPresent(action->{ - val uiAction = new Button(action.getName()); + + val owner = objectInteractor.getManagedObject(); + ManagedAction.getActionHandle(owner, actionData.getId()) + .checkVisibility(Where.OBJECT_FORMS) + .get() + .ifPresent(managedAction -> { + // TODO yet not doing anything + val uiAction = new Button(managedAction.getName()); container.add(uiAction); uiAction.getStyle().set("margin-left", "0.5em"); uiAction.addThemeVariants( ButtonVariant.LUMO_SMALL); - }); } @Override protected void onProperty(HasComponents container, PropertyLayoutData propertyData) { - objectInteractor - .getPropertyBinding(propertyData.getId(), Where.OBJECT_FORMS) - .ifPresent(propertyBinding->{ + + val owner = objectInteractor.getManagedObject(); + + ManagedProperty.getPropertyHandle(owner, propertyData.getId()) + .checkVisibility(Where.OBJECT_FORMS) + .get() + .ifPresent(managedProperty -> { val uiProperty = uiComponentFactory - .componentFor(UiComponentFactory.Request.of(propertyBinding)); - container.add(uiProperty); + .componentFor(UiComponentFactory.Request.of(managedProperty)); + container.add(uiProperty); }); } @Override protected void onCollection(HasComponents container, CollectionLayoutData collectionData) { - objectInteractor.lookupCollection(collectionData.getId()) - .ifPresent(collection->{ - container.add(new H3(collection.getName())); - - val collectionValue = Optional.ofNullable(collection.get(managedObject)) - .orElse(ManagedObject.of(collection.getSpecification(), null)); - container.add(createCollectionComponent(collection, collectionValue)); + + val owner = objectInteractor.getManagedObject(); + + ManagedCollection.getCollectionHandle(owner, collectionData.getId()) + .checkVisibility(Where.OBJECT_FORMS) + .get() + .ifPresent(managedCollection -> { + container.add(new H3(managedCollection.getName())); + + val uiCollection = createCollectionComponent(managedCollection); + container.add(uiCollection); }); + } }; @@ -243,13 +257,12 @@ public class ObjectFormView extends VerticalLayout { // -- HELPER private Component createCollectionComponent( - final ObjectAssociation objectAssociation, - final ManagedObject assocObject) { + final ManagedCollection managedCollection) { - val labelLiteral = "Collection: " + objectAssociation.getName(); - val pojo = assocObject.getPojo(); + val labelLiteral = "Collection: " + managedCollection.getName(); + val pojo = managedCollection.getCollectionValue().getPojo(); if (pojo instanceof Collection) { - return TableView.fromObjectAssociation(objectAssociation, assocObject); + return TableView.fromManagedCollection(managedCollection); } if (pojo == null) { diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java index fbff986..ef6892e 100644 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java +++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java @@ -30,7 +30,8 @@ import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.ObjectFeature; -import org.apache.isis.viewer.common.model.binding.interaction.PropertyBinding; +import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto; +import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty; import lombok.NonNull; import lombok.Value; @@ -47,21 +48,15 @@ public interface UiComponentFactory<T> { /** not null but the wrapped pojo is allowed to be null*/ @NonNull private final ManagedObject managedObject; @NonNull private final ObjectFeature objectFeature; - @NonNull private final Function<ManagedObject, String> toDomainPropagator; + @NonNull private final Function<ManagedObject, Optional<InteractionVeto> > toDomainPropagator; - public static Request of(PropertyBinding propertyBinding) { + public static Request of(ManagedProperty propertyBinding) { val property = propertyBinding.getProperty(); //TODO there is also the aspect of editable or not val propertyValue = propertyBinding.getPropertyValue(); return of(propertyValue, property, proposedNewValue -> { - - val iResponse = propertyBinding.modifyProperty(proposedNewValue); - if (iResponse.isFailure()) { - return iResponse.getFailureMessage(); // validation result if any - } - return null; - + return propertyBinding.modifyProperty(proposedNewValue); }); } @@ -135,7 +130,7 @@ public interface UiComponentFactory<T> { .map(type::cast); } - public <T> Function<T, String> getPropagator(Class<T> pojoType) { + public <T> Function<T, Optional<InteractionVeto>> getPropagator(Class<T> pojoType) { return (T newValueProposal)->{ //TODO we are loosing any fields that are cached within ManagedObject val newValue = ManagedObject.of(getFeatureSpec(), newValueProposal); diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java deleted file mode 100644 index a082ead..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import java.util.function.Consumer; - -import org.apache.isis.core.commons.internal.base._Either; -import org.apache.isis.core.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.spec.feature.ObjectAction; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class ActionAccessChain { - - @NonNull private _Either<ActionBinding, InteractionResponse> chain; - -// public ActionAccessChain modifyProperty( -// @NonNull final Function<OneToOneAssociation, ManagedObject> newProperyValueProvider) { -// -// chain = chain.leftRemap(propertyBinding->{ -// final InteractionResponse iResponse = propertyBinding.modifyProperty(newProperyValueProvider); -// if(iResponse.isFailure()) { -// _Either.right(iResponse); -// } -// return _Either.left(propertyBinding); -// }); -// -// return this; -// } - - public ActionAccessChain onFailure(Consumer<InteractionResponse> onFailure) { - chain.right().ifPresent(onFailure); - return this; - } - - public ActionBinding getBinding() { - return chain.left() - .orElseThrow(_Exceptions::noSuchElement); - } - - public ObjectAction getAction() { - return chain.left() - .map(ActionBinding::getAction) - .orElseThrow(_Exceptions::noSuchElement); - } - - public ActionAccessChain ifPresent(Consumer<ActionBinding> actionBindingConsumer) { - chain.left().ifPresent(actionBindingConsumer::accept); - return this; - } - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java deleted file mode 100644 index 45aee47..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.ObjectAction; - -public interface ActionBinding { - - ManagedObject getManagedObject(); - ObjectAction getAction(); - -// InteractionResponse modifyProperty(ManagedObject newProperyValue); -// -// default InteractionResponse modifyProperty( -// @NonNull final Function<OneToOneAssociation, ManagedObject> newProperyValueProvider) { -// return modifyProperty(newProperyValueProvider.apply(getProperty())); -// } -// -// default ManagedObject getPropertyValue() { -// val property = getProperty(); -// -// return Optional.ofNullable(property.get(getManagedObject())) -// .orElse(ManagedObject.of(property.getSpecification(), null)); -// } - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java deleted file mode 100644 index 09f5b55..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.ObjectAction; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class ActionBindingReadonly implements ActionBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final ObjectAction action; - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java deleted file mode 100644 index 2bdc815..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.ObjectAction; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class ActionBindingUsable implements ActionBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final ObjectAction action; - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java deleted file mode 100644 index e00fff7..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import java.util.function.Consumer; - -import org.apache.isis.core.commons.internal.base._Either; -import org.apache.isis.core.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class CollectionAccessChain { - - @NonNull private _Either<CollectionBinding, InteractionResponse> chain; - -// public CollectionAccessChain modifyProperty( -// @NonNull final Function<OneToManyAssociation, ManagedObject> newProperyValueProvider) { -// -// chain = chain.leftRemap(propertyBinding->{ -// final InteractionResponse iResponse = propertyBinding.modifyProperty(newProperyValueProvider); -// if(iResponse.isFailure()) { -// _Either.right(iResponse); -// } -// return _Either.left(propertyBinding); -// }); -// -// return this; -// } - - public CollectionAccessChain onFailure(Consumer<InteractionResponse> onFailure) { - chain.right().ifPresent(onFailure); - return this; - } - - public CollectionBinding getBinding() { - return chain.left() - .orElseThrow(_Exceptions::noSuchElement); - } - - public OneToManyAssociation getCollection() { - return chain.left() - .map(CollectionBinding::getCollection) - .orElseThrow(_Exceptions::noSuchElement); - } - - public CollectionAccessChain ifPresent(Consumer<CollectionBinding> collectionBindingConsumer) { - chain.left().ifPresent(collectionBindingConsumer::accept); - return this; - } - - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java deleted file mode 100644 index 92de7cd..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; - -public interface CollectionBinding { - - ManagedObject getManagedObject(); - OneToManyAssociation getCollection(); - -// InteractionResponse modifyProperty(ManagedObject newProperyValue); -// -// default InteractionResponse modifyProperty( -// @NonNull final Function<OneToOneAssociation, ManagedObject> newProperyValueProvider) { -// return modifyProperty(newProperyValueProvider.apply(getProperty())); -// } -// -// default ManagedObject getPropertyValue() { -// val property = getProperty(); -// -// return Optional.ofNullable(property.get(getManagedObject())) -// .orElse(ManagedObject.of(property.getSpecification(), null)); -// } - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java deleted file mode 100644 index 877e654..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class CollectionBindingEditable implements CollectionBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final OneToManyAssociation collection; - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java deleted file mode 100644 index d2a4b03..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class CollectionBindingReadonly implements CollectionBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final OneToManyAssociation collection; - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java index 64f2a81..20931d2 100644 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java +++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java @@ -18,62 +18,14 @@ */ package org.apache.isis.viewer.common.model.binding.interaction; -import java.util.Objects; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; - -import org.apache.isis.applib.annotation.Where; -import org.apache.isis.core.commons.internal.base._Either; -import org.apache.isis.core.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.spec.feature.Contributed; -import org.apache.isis.core.metamodel.spec.feature.ObjectAction; -import org.apache.isis.core.metamodel.spec.feature.ObjectMember; -import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; -import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.val; @RequiredArgsConstructor(staticName = "bind") public class ObjectBinding { - // only used to create failure messages - @RequiredArgsConstructor - private static enum MemberType { - PROPERTY(OneToOneAssociation.class, (spec, propertyId)-> - spec.getAssociation(propertyId) - .map(property->property.isOneToOneAssociation()?property:null)), - - COLLECTION(OneToManyAssociation.class, (spec, collectionId)-> - spec.getAssociation(collectionId) - .map(collection->collection.isOneToManyAssociation()?collection:null)), - - ACTION(ObjectAction.class, (spec, actionId)-> - spec.getObjectAction(actionId)); - - @Getter private final Class<? extends ObjectMember> memberType; - @Getter private final BiFunction< - ObjectSpecification, String, - Optional<? extends ObjectMember> - > memberProvider; - - } - - public static enum AccessIntent { - ACCESS, MUTATE; - - public boolean isMutate() { - return this == MUTATE; - } - } - @Getter private final ManagedObject managedObject; @@ -81,319 +33,49 @@ public class ObjectBinding { return managedObject.getSpecification().getTitle(null, managedObject); } - @Deprecated - public Stream<OneToOneAssociation> streamVisisbleProperties() { - return managedObject.getSpecification() - .streamAssociations(Contributed.INCLUDED) - .filter(objMember->objMember.getFeatureType().isProperty()) - //TODO filter visibility - .map(OneToOneAssociation.class::cast); - } - - @Deprecated - public Stream<OneToManyAssociation> streamVisisbleCollections() { - return managedObject.getSpecification() - .streamAssociations(Contributed.INCLUDED) - .filter(objMember->objMember.getFeatureType().isCollection()) - //TODO filter visibility - .map(OneToManyAssociation.class::cast); - } - - @Deprecated - public Stream<ObjectAction> streamVisisbleActions() { - return managedObject.getSpecification() - .streamObjectActions(Contributed.INCLUDED) - .filter(objMember->objMember.getFeatureType().isAction()) - //TODO filter visibility - .map(ObjectAction.class::cast); - } - - public Optional<ObjectAction> lookupAction(String actionId) { - return streamVisisbleActions() - .filter(action->Objects.equals(actionId, action.getId())) - .findFirst(); - } - - public Optional<OneToOneAssociation> lookupVisibleProperty(String propertyId) { - return streamVisisbleProperties() - .filter(property->Objects.equals(propertyId, property.getId())) - .findFirst(); - } - - public Optional<OneToManyAssociation> lookupCollection(String collectionId) { - return streamVisisbleCollections() - .filter(collection->Objects.equals(collectionId, collection.getId())) - .findFirst(); - } - - /** - * @param actionId - * @param where - * @return ActionAccessChain with either an editable or readonly ActionBinding, - * based on visibility and usability - */ - public ActionAccessChain getActionBinding(String actionId, Where where) { - val either = getActionThatIsVisible(actionId, where) - .map( - action-> - checkUsability(action, where).isSuccess() - ? ActionBindingUsable.of(managedObject, action) - : ActionBindingReadonly.of(managedObject, action), - UnaryOperator.identity()); - return ActionAccessChain.of(either); - } - - /** - * - * @param actionId - * @param where - * @param accessIntent - * @return ActionAccessChain with either an editable or readonly PropertyBinding, - * based on visibility and accessIntent - */ - public ActionAccessChain getActionBinding( - final String actionId, - final Where where, - final AccessIntent accessIntent) { - - val either = getActionThatIsVisibleForIntent(actionId, where, accessIntent) - .map( - action->{ - switch(accessIntent) { - case ACCESS: - return ActionBindingReadonly.of(managedObject, action); - case MUTATE: - return ActionBindingUsable.of(managedObject, action); - default: - throw _Exceptions.unmatchedCase(accessIntent); - } - }, - UnaryOperator.identity()); - return ActionAccessChain.of(either); - } - - /** - * @param propertyId - * @param where - * @return PropertyAccessChain with either an editable or readonly PropertyBinding, - * based on visibility and usability - */ - public PropertyAccessChain getPropertyBinding(String propertyId, Where where) { - val either = getPropertyThatIsVisible(propertyId, where) - .map( - property-> - checkUsability(property, where).isSuccess() - ? PropertyBindingEditable.of(managedObject, property) - : PropertyBindingReadonly.of(managedObject, property), - UnaryOperator.identity()); - return PropertyAccessChain.of(either); - } - - /** - * - * @param propertyId - * @param where - * @param accessIntent - * @return PropertyAccessChain with either an editable or readonly PropertyBinding, - * based on visibility and accessIntent - */ - public PropertyAccessChain getPropertyBinding( - final String propertyId, - final Where where, - final AccessIntent accessIntent) { - - val either = getPropertyThatIsVisibleForIntent(propertyId, where, accessIntent) - .map( - property->{ - switch(accessIntent) { - case ACCESS: - return PropertyBindingReadonly.of(managedObject, property); - case MUTATE: - return PropertyBindingEditable.of(managedObject, property); - default: - throw _Exceptions.unmatchedCase(accessIntent); - } - }, - UnaryOperator.identity()); - return PropertyAccessChain.of(either); - } - - /** - * @param collectionId - * @param where - * @return CollectionAccessChain with either an editable or readonly CollectionBinding, - * based on visibility and usability - */ - public CollectionAccessChain getCollectionBinding(String collectionId, Where where) { - val either = getCollectionThatIsVisible(collectionId, where) - .map( - coll-> - checkUsability(coll, where).isSuccess() - ? CollectionBindingEditable.of(managedObject, coll) - : CollectionBindingReadonly.of(managedObject, coll), - UnaryOperator.identity()); - - return CollectionAccessChain.of(either); - } - - /** - * - * @param collectionId - * @param where - * @param accessIntent - * @return CollectionAccessChain with either an editable or readonly CollectionBinding, - * based on visibility and accessIntent - */ - public CollectionAccessChain getCollectionBinding( - final String collectionId, - final Where where, - final AccessIntent accessIntent) { - - val either = getCollectionThatIsVisibleForIntent(collectionId, where, accessIntent) - .map( - coll->{ - switch(accessIntent) { - case ACCESS: - return CollectionBindingReadonly.of(managedObject, coll); - case MUTATE: - return CollectionBindingEditable.of(managedObject, coll); - default: - throw _Exceptions.unmatchedCase(accessIntent); - } - }, - UnaryOperator.identity()); - - return CollectionAccessChain.of(either); - - } - - // -- HELPER - TOP LEVEL - - private _Either<ObjectAction, InteractionResponse> - getActionThatIsVisible(String actionId, Where where) { - val chain = this.<ObjectAction>startChain(MemberType.ACTION, actionId); - return checkVisibility(chain, MemberType.ACTION, where); - } - - private _Either<OneToOneAssociation, InteractionResponse> - getPropertyThatIsVisible(String propertyId, Where where) { - val chain = this.<OneToOneAssociation>startChain(MemberType.PROPERTY, propertyId); - return checkVisibility(chain, MemberType.PROPERTY, where); - } - - private _Either<OneToManyAssociation, InteractionResponse> - getCollectionThatIsVisible(String collectionId, Where where) { - val chain = this.<OneToManyAssociation>startChain(MemberType.COLLECTION, collectionId); - return checkVisibility(chain, MemberType.COLLECTION, where); - } - - private _Either<ObjectAction, InteractionResponse> getActionThatIsVisibleForIntent( - final String actionId, - final Where where, - final AccessIntent accessIntent) { - return checkUsability(getActionThatIsVisible(actionId, where), where, accessIntent); - } - - private _Either<OneToOneAssociation, InteractionResponse> getPropertyThatIsVisibleForIntent( - final String propertyId, - final Where where, - final AccessIntent accessIntent) { - return checkUsability(getPropertyThatIsVisible(propertyId, where), where, accessIntent); - } - - private _Either<OneToManyAssociation, InteractionResponse> getCollectionThatIsVisibleForIntent( - final String propertyId, - final Where where, - final AccessIntent accessIntent) { - return checkUsability(getCollectionThatIsVisible(propertyId, where), where, accessIntent); - } - - // HELPER - MID LEVEL - - private <T extends ObjectMember> _Either<T, InteractionResponse> startChain( - final MemberType memberType, - final String memberId) { - - val spec = managedObject.getSpecification(); - val member = memberType.getMemberProvider().apply(spec, memberId); - - if(!member.isPresent()) { - return _Either.right(InteractionResponse.failed( - Veto.NOT_FOUND, - notFound(memberType, memberId))); - } - return _Either.<T, InteractionResponse>left((T)member.get()); - } - - private <T extends ObjectMember> _Either<T, InteractionResponse> checkVisibility( - final _Either<T, InteractionResponse> chain, - final MemberType memberType, - final Where where) { - - return chain.leftRemap(objectMember->{ - val visibility = checkVisibility(memberType, objectMember, where); - if(visibility.isFailure()) { - return _Either.right(visibility); - } - return _Either.left(objectMember); - - }); - } - - private <T extends ObjectMember> _Either<T, InteractionResponse> checkUsability( - final _Either<T, InteractionResponse> chain, - final Where where, - final AccessIntent accessIntent) { - - return chain.leftRemap(objectMember->{ - if(accessIntent.isMutate()) { - val usability = checkUsability(objectMember, where); - if(usability.isFailure()) { - return _Either.right(usability); - } - } - return _Either.left(objectMember); - - }); - } - - // HELPER - LOWEST LEVEL - - private <T extends ObjectMember> InteractionResponse checkVisibility( - final MemberType memberType, - final T objectMember, - final Where where) { - - val visibilityConsent = - objectMember.isVisible( - managedObject, InteractionInitiatedBy.USER, where); - return visibilityConsent.isVetoed() - ? InteractionResponse.failed( - Veto.HIDDEN, - notFound(memberType, objectMember.getId())) - : InteractionResponse.success(); - } - - private <T extends ObjectMember> InteractionResponse checkUsability( - final T objectMember, - final Where where) { - - val usabilityConsent = - objectMember.isUsable( - managedObject, InteractionInitiatedBy.USER, where); - return usabilityConsent.isVetoed() - ? InteractionResponse.failed( - Veto.FORBIDDEN, - usabilityConsent.getReason()) - : InteractionResponse.success(); - } - - private <T extends ObjectMember> String notFound( - MemberType memberType, - String memberId) { - return String.format("%s '%s' either does not exist, is disabled or is not visible", - memberId, - memberType.name().toLowerCase()); - } +// @Deprecated +// public Stream<OneToOneAssociation> streamVisisbleProperties() { +// return managedObject.getSpecification() +// .streamAssociations(Contributed.INCLUDED) +// .filter(objMember->objMember.getFeatureType().isProperty()) +// //TODO filter visibility +// .map(OneToOneAssociation.class::cast); +// } +// +// @Deprecated +// public Stream<OneToManyAssociation> streamVisisbleCollections() { +// return managedObject.getSpecification() +// .streamAssociations(Contributed.INCLUDED) +// .filter(objMember->objMember.getFeatureType().isCollection()) +// //TODO filter visibility +// .map(OneToManyAssociation.class::cast); +// } +// +// @Deprecated +// public Stream<ObjectAction> streamVisisbleActions() { +// return managedObject.getSpecification() +// .streamObjectActions(Contributed.INCLUDED) +// .filter(objMember->objMember.getFeatureType().isAction()) +// //TODO filter visibility +// .map(ObjectAction.class::cast); +// } +// +// public Optional<ObjectAction> lookupAction(String actionId) { +// return streamVisisbleActions() +// .filter(action->Objects.equals(actionId, action.getId())) +// .findFirst(); +// } +// +// public Optional<OneToOneAssociation> lookupVisibleProperty(String propertyId) { +// return streamVisisbleProperties() +// .filter(property->Objects.equals(propertyId, property.getId())) +// .findFirst(); +// } +// +// public Optional<OneToManyAssociation> lookupCollection(String collectionId) { +// return streamVisisbleCollections() +// .filter(collection->Objects.equals(collectionId, collection.getId())) +// .findFirst(); +// } } diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java deleted file mode 100644 index 633f632..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import java.util.function.Consumer; -import java.util.function.Function; - -import org.apache.isis.core.commons.internal.base._Either; -import org.apache.isis.core.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class PropertyAccessChain { - - @NonNull private _Either<PropertyBinding, InteractionResponse> chain; - - public PropertyAccessChain modifyProperty( - @NonNull final Function<OneToOneAssociation, ManagedObject> newProperyValueProvider) { - - chain = chain.leftRemap(propertyBinding->{ - final InteractionResponse iResponse = propertyBinding.modifyProperty(newProperyValueProvider); - if(iResponse.isFailure()) { - _Either.right(iResponse); - } - return _Either.left(propertyBinding); - }); - - return this; - } - - public PropertyAccessChain onFailure(Consumer<InteractionResponse> onFailure) { - chain.right().ifPresent(onFailure); - return this; - } - - public PropertyBinding getBinding() { - return chain.left() - .orElseThrow(_Exceptions::noSuchElement); - } - - public OneToOneAssociation getProperty() { - return chain.left() - .map(PropertyBinding::getProperty) - .orElseThrow(_Exceptions::noSuchElement); - } - - public PropertyAccessChain ifPresent(Consumer<PropertyBinding> propertyBindingConsumer) { - chain.left().ifPresent(propertyBindingConsumer::accept); - return this; - } - - - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java deleted file mode 100644 index 690e8bb..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import java.util.Optional; -import java.util.function.Function; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; - -import lombok.NonNull; -import lombok.val; - -public interface PropertyBinding { - - ManagedObject getManagedObject(); - OneToOneAssociation getProperty(); - - InteractionResponse modifyProperty(ManagedObject newProperyValue); - - default InteractionResponse modifyProperty( - @NonNull final Function<OneToOneAssociation, ManagedObject> newProperyValueProvider) { - return modifyProperty(newProperyValueProvider.apply(getProperty())); - } - - default ManagedObject getPropertyValue() { - val property = getProperty(); - - return Optional.ofNullable(property.get(getManagedObject())) - .orElse(ManagedObject.of(property.getSpecification(), null)); - } - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java deleted file mode 100644 index d7f54e1..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import javax.annotation.Nullable; - -import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.val; - -@RequiredArgsConstructor(staticName = "of") -public class PropertyBindingEditable implements PropertyBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final OneToOneAssociation property; - - - @Override - public InteractionResponse modifyProperty(@Nullable ManagedObject proposedNewValue) { - - val consent = property.isAssociationValid(getManagedObject(), proposedNewValue, InteractionInitiatedBy.USER); - if (consent.isVetoed()) { - return InteractionResponse.failed(Veto.UNAUTHORIZED, consent.getReason()); - } - - property.set(managedObject, proposedNewValue, InteractionInitiatedBy.USER); - - return InteractionResponse.success(); - } - -} diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java deleted file mode 100644 index 3c8ed80..0000000 --- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.apache.isis.viewer.common.model.binding.interaction; - -import javax.annotation.Nullable; - -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public class PropertyBindingReadonly implements PropertyBinding { - - @Getter(onMethod = @__(@Override)) - private final ManagedObject managedObject; - - @Getter(onMethod = @__(@Override)) - private final OneToOneAssociation property; - - @Override - public InteractionResponse modifyProperty(@Nullable ManagedObject newProperyValue) { - // if this code is ever reached we have wired up something wrong internally - return InteractionResponse.failed(Veto.FORBIDDEN); - } - -} diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java index 4bd5c3a..eaa94c3 100644 --- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java +++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java @@ -59,8 +59,8 @@ import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.ObjectSpecId; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; +import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty; import org.apache.isis.core.runtime.iactn.IsisInteractionTracker; -import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; import org.apache.isis.viewer.restfulobjects.applib.Rel; import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; @@ -74,6 +74,7 @@ import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.MemberReprM import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService; import org.apache.isis.viewer.restfulobjects.rendering.util.Util; import org.apache.isis.viewer.restfulobjects.viewer.context.ResourceContext; +import org.apache.isis.viewer.restfulobjects.viewer.resources.ObjectAdapterAccessHelper.AccessIntent; import lombok.val; import lombok.extern.log4j.Log4j2; @@ -451,19 +452,16 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements val objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); - ObjectBinding.bind(objectAdapter) - .getPropertyBinding( - propertyId, - resourceContext.getWhere(), - ObjectBinding.AccessIntent.MUTATE) + ManagedProperty.getPropertyHandle(objectAdapter, propertyId) + .checkVisibility(resourceContext.getWhere()) + .checkUsability(resourceContext.getWhere()) // only when ObjectBinding.AccessIntent.MUTATE .modifyProperty(property->{ val proposedNewValue = new JsonParserHelper(resourceContext, property.getSpecification()) .parseAsMapWithSingleValue(Util.asStringUtf8(body)); return proposedNewValue; }) - .onFailure(InteractionFailureHandler::onFailure) - ; + .getOrElseThrow(InteractionFailureHandler::onFailure); val domainResourceHelper = DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter); return domainResourceHelper.propertyDetails( @@ -489,13 +487,11 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements val objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); - ObjectBinding.bind(objectAdapter) - .getPropertyBinding( - propertyId, - resourceContext.getWhere(), - ObjectBinding.AccessIntent.MUTATE) + ManagedProperty.getPropertyHandle(objectAdapter, propertyId) + .checkVisibility(resourceContext.getWhere()) + .checkUsability(resourceContext.getWhere()) // only when ObjectBinding.AccessIntent.MUTATE .modifyProperty(property->null) - .onFailure(InteractionFailureHandler::onFailure); + .getOrElseThrow(InteractionFailureHandler::onFailure); val domainResourceHelper = DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter); return domainResourceHelper.propertyDetails( @@ -551,7 +547,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements final ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent( - collectionId, ObjectBinding.AccessIntent.MUTATE); + collectionId, AccessIntent.MUTATE); if (!collection.getCollectionSemantics().isAnySet()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Collection '%s' does not have set semantics", collectionId); @@ -590,7 +586,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements final ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent( - collectionId, ObjectBinding.AccessIntent.MUTATE); + collectionId, AccessIntent.MUTATE); if (!collection.getCollectionSemantics().isListOrArray()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Collection '%s' does not have list or array semantics", collectionId); @@ -629,7 +625,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements final ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent( - collectionId, ObjectBinding.AccessIntent.MUTATE); + collectionId, AccessIntent.MUTATE); final ObjectSpecification collectionSpec = collection.getSpecification(); final ManagedObject argAdapter = new JsonParserHelper(resourceContext, collectionSpec) diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java index 56dee24..9315f65 100644 --- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java +++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java @@ -27,7 +27,6 @@ import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; import org.apache.isis.viewer.restfulobjects.applib.RestfulResponse; import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext; @@ -43,6 +42,7 @@ import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndCo import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndProperty2; import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService; import org.apache.isis.viewer.restfulobjects.viewer.context.ResourceContext; +import org.apache.isis.viewer.restfulobjects.viewer.resources.ObjectAdapterAccessHelper.AccessIntent; import lombok.val; @@ -109,7 +109,7 @@ class DomainResourceHelper { ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent(propertyId, ObjectBinding.AccessIntent.ACCESS); + final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent(propertyId, AccessIntent.ACCESS); transactionService.flushTransaction(); return representationService.propertyDetails(resourceContext, new ObjectAndProperty2(objectAdapter, property, memberMode), memberMode); @@ -127,7 +127,7 @@ class DomainResourceHelper { ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent(collectionId, ObjectBinding.AccessIntent.ACCESS); + final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent(collectionId, AccessIntent.ACCESS); transactionService.flushTransaction(); return representationService.collectionDetails(resourceContext, new ObjectAndCollection2(objectAdapter, collection, memberMode), memberMode); @@ -143,7 +143,7 @@ class DomainResourceHelper { ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, ObjectBinding.AccessIntent.ACCESS); + final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, AccessIntent.ACCESS); transactionService.flushTransaction(); return representationService.actionPrompt(resourceContext, new ObjectAndAction(objectAdapter, action)); @@ -163,7 +163,7 @@ class DomainResourceHelper { final ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, ObjectBinding.AccessIntent.MUTATE); + final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, AccessIntent.MUTATE); final SemanticsOf actionSemantics = action.getSemantics(); if (! actionSemantics.isSafeInNature()) { @@ -187,7 +187,7 @@ class DomainResourceHelper { final ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, ObjectBinding.AccessIntent.MUTATE); + final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, AccessIntent.MUTATE); final SemanticsOf actionSemantics = action.getSemantics(); if (!actionSemantics.isIdempotentInNature()) { @@ -205,7 +205,7 @@ class DomainResourceHelper { ObjectAdapterAccessHelper accessHelper = ObjectAdapterAccessHelper.of(resourceContext, objectAdapter); - final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, ObjectBinding.AccessIntent.MUTATE); + final ObjectAction action = accessHelper.getObjectActionThatIsVisibleForIntent(actionId, AccessIntent.MUTATE); return invokeActionUsingAdapters(action, arguments, ActionResultReprRenderer.SelfLink.EXCLUDED); } diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java index 4251651..426fbbc 100644 --- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java +++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java @@ -18,30 +18,64 @@ */ package org.apache.isis.viewer.restfulobjects.viewer.resources; -import org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse; +import javax.annotation.Nullable; + +import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto; import org.apache.isis.viewer.restfulobjects.applib.RestfulResponse; import org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException; public class InteractionFailureHandler { - public static void onFailure(final InteractionResponse failure) { + public static RestfulObjectsApplicationException onFailure(@Nullable final InteractionVeto failure) { - if(failure==null || failure.isSuccess()) { - return; + if(failure==null) { + return RestfulObjectsApplicationException + .createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR, + "unexpected empty failure holder"); } - switch(failure.getVeto()) { + switch(failure.getVetoType()) { case NOT_FOUND: case HIDDEN: - throw RestfulObjectsApplicationException + return RestfulObjectsApplicationException .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND, - failure.getFailureMessage()); - case UNAUTHORIZED: - case FORBIDDEN: - throw RestfulObjectsApplicationException + failure.getReason()); + case READONLY: + case INVALID: + return RestfulObjectsApplicationException .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN, - failure.getFailureMessage()); + failure.getReason()); } + + return RestfulObjectsApplicationException + .createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR, + "unmatched veto type " + failure.getVetoType()); + } + +// public static void onFailure(@Nullable final InteractionVeto failure) { +// +// if(failure==null) { +// return; +// } +// +// switch(failure.getVetoType()) { +// case NOT_FOUND: +// case HIDDEN: +// throw RestfulObjectsApplicationException +// .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND, +// failure.getReason()); +// case READONLY: +// case INVALID: +// throw RestfulObjectsApplicationException +// .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN, +// failure.getReason()); +// } +// +// } + + + + } diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java index fee1000..0bdf621 100644 --- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java +++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java @@ -23,11 +23,13 @@ import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding; -import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding.AccessIntent; +import org.apache.isis.core.metamodel.spec.interaction.ManagedAction; +import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection; +import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty; import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext; import lombok.RequiredArgsConstructor; +import lombok.val; /** * Utility class that encapsulates the logic for checking access to the specified @@ -35,40 +37,73 @@ import lombok.RequiredArgsConstructor; */ @RequiredArgsConstructor public class ObjectAdapterAccessHelper { + + public static enum AccessIntent { + ACCESS, MUTATE; + + public boolean isMutate() { + return this == MUTATE; + } + } public static ObjectAdapterAccessHelper of( final IResourceContext resourceContext, final ManagedObject managedObject) { return new ObjectAdapterAccessHelper( - ObjectBinding.bind(managedObject), + managedObject, resourceContext.getWhere()); } - private final ObjectBinding objectInteractor; + private final ManagedObject managedObject; private final Where where; + + public ObjectAction getObjectActionThatIsVisibleForIntent( + final String actionId, final AccessIntent intent) { + + val actionHandle = ManagedAction + .getActionHandle(managedObject, actionId) + .checkVisibility(where); + + if(intent.isMutate()) { + actionHandle.checkUsability(where); + } + + return actionHandle + .getOrElseThrow(InteractionFailureHandler::onFailure) + .getAction(); + } public OneToOneAssociation getPropertyThatIsVisibleForIntent( final String propertyId, final AccessIntent intent) { - - return objectInteractor.getPropertyBinding(propertyId, where, intent) - .onFailure(InteractionFailureHandler::onFailure) - .getProperty(); + + val propertyHandle = ManagedProperty + .getPropertyHandle(managedObject, propertyId) + .checkVisibility(where); + + if(intent.isMutate()) { + propertyHandle.checkUsability(where); + } + + return propertyHandle + .getOrElseThrow(InteractionFailureHandler::onFailure) + .getProperty(); + } public OneToManyAssociation getCollectionThatIsVisibleForIntent( final String collectionId, final AccessIntent intent) { - return objectInteractor.getCollectionBinding(collectionId, where, intent) - .onFailure(InteractionFailureHandler::onFailure) - .getCollection(); - } - - public ObjectAction getObjectActionThatIsVisibleForIntent( - final String actionId, final AccessIntent intent) { + val collectionHandle = ManagedCollection + .getCollectionHandle(managedObject, collectionId) + .checkVisibility(where); + + if(intent.isMutate()) { + collectionHandle.checkUsability(where); + } - return objectInteractor.getActionBinding(actionId, where, intent) - .onFailure(InteractionFailureHandler::onFailure) - .getAction(); + return collectionHandle + .getOrElseThrow(InteractionFailureHandler::onFailure) + .getCollection(); }
