Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gh-3237 Implement DeleteElements for Map Store #3239

Merged
merged 19 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Crown Copyright
* Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,10 @@
import uk.gov.gchq.gaffer.commonutil.iterable.StreamIterable;
import uk.gov.gchq.gaffer.commonutil.iterable.StreamIterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
Expand Down Expand Up @@ -122,6 +125,35 @@
return StreamSupport.stream(Spliterators.spliterator(array, 0), true);
}

/**
* Convert an iterable to a {@link java.util.stream.Stream}
* of {@link java.util.List}.
*
* @param iterable the input iterable
* @param size the size of the batch
* @param <T> the type of object stored in the stream
* @return a stream containing the contents of the iterable in a list
*/
public static <T> Stream<List<T>> toBatches(final Iterable<T> iterable, final int size) {
cn337131 marked this conversation as resolved.
Show resolved Hide resolved
Iterator<T> iterator = iterable.iterator();
Iterator<List<T>> chunksIterator = new Iterator<List<T>>() {

Check warning on line 139 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L138-L139

Added lines #L138 - L139 were not covered by tests
@Override
public boolean hasNext() {
return iterator.hasNext();

Check warning on line 142 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L142

Added line #L142 was not covered by tests
}

@Override
public List<T> next() {
List<T> result = new ArrayList<>(size);

Check warning on line 147 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L147

Added line #L147 was not covered by tests
for (int i = 0; i < size && iterator.hasNext(); i++) {
result.add(iterator.next());

Check warning on line 149 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L149

Added line #L149 was not covered by tests
}
return result;

Check warning on line 151 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L151

Added line #L151 was not covered by tests
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(chunksIterator, Spliterator.IMMUTABLE), false);

Check warning on line 154 in core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java

View check run for this annotation

Codecov / codecov/patch

core/common-util/src/main/java/uk/gov/gchq/gaffer/commonutil/stream/Streams.java#L154

Added line #L154 was not covered by tests
}

private Streams() {
// Private constructor to prevent instantiation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import uk.gov.gchq.gaffer.operation.graph.OperationView;
import uk.gov.gchq.gaffer.operation.impl.Limit;
import uk.gov.gchq.gaffer.operation.impl.add.AddElements;
import uk.gov.gchq.gaffer.operation.impl.delete.DeleteElements;
import uk.gov.gchq.gaffer.operation.impl.get.GetAdjacentIds;
import uk.gov.gchq.gaffer.operation.impl.get.GetAllElements;
import uk.gov.gchq.gaffer.operation.impl.get.GetElements;
Expand Down Expand Up @@ -2713,6 +2714,11 @@ protected OperationHandler<? extends AddElements> getAddElementsHandler() {
return null;
}

@Override
protected OperationHandler<? extends DeleteElements> getDeleteElementsHandler() {
return null;
}

@Override
protected OutputOperationHandler<GetTraits, Set<StoreTrait>> getGetTraitsHandler() {
return new GetTraitsHandler(new HashSet<>(0));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 Crown Copyright
* Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@
import uk.gov.gchq.gaffer.operation.OperationChain;
import uk.gov.gchq.gaffer.operation.OperationException;
import uk.gov.gchq.gaffer.operation.impl.add.AddElements;
import uk.gov.gchq.gaffer.operation.impl.delete.DeleteElements;
import uk.gov.gchq.gaffer.operation.impl.get.GetAdjacentIds;
import uk.gov.gchq.gaffer.operation.impl.get.GetAllElements;
import uk.gov.gchq.gaffer.operation.impl.get.GetElements;
Expand Down Expand Up @@ -83,6 +84,11 @@ protected OperationHandler<? extends AddElements> getAddElementsHandler() {
return null;
}

@Override
protected OperationHandler<? extends DeleteElements> getDeleteElementsHandler() {
return null;
}

@Override
protected OutputOperationHandler<GetTraits, Set<StoreTrait>> getGetTraitsHandler() {
return mock(GetTraitsHandler.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright 2024 Crown Copyright
*
* Licensed 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 uk.gov.gchq.gaffer.operation.impl.delete;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import uk.gov.gchq.gaffer.commonutil.ToStringBuilder;
import uk.gov.gchq.gaffer.data.element.Element;
import uk.gov.gchq.gaffer.operation.Operation;
import uk.gov.gchq.gaffer.operation.Validatable;
import uk.gov.gchq.gaffer.operation.io.MultiInput;
import uk.gov.gchq.koryphe.Since;
import uk.gov.gchq.koryphe.Summary;

import java.util.Map;

/**
* A {@code DeleteElements} operation is a {@link Validatable} operation
* for deleting elements. This is a core operation that all stores should
* be able to handle.
*
* This operation requires an {@link Iterable} of {@link Element}s to be deleted.
* Handlers should throw an {@link uk.gov.gchq.gaffer.operation.OperationException}
* if unsuccessful.
*
* For normal operation handlers the operation
* {@link uk.gov.gchq.gaffer.data.elementdefinition.view.View} will be ignored.
*
* @see DeleteElements.Builder
*/
@JsonPropertyOrder(value = { "class", "elements" }, alphabetic = true)
@Since("2.3.0")
@Summary("Deletes elements")
public class DeleteElements implements
Validatable,
MultiInput<Element> {
private boolean validate = true;
private boolean skipInvalidElements;
private Iterable<? extends Element> elements;
private Map<String, String> options;

@Override
public boolean isValidate() {
return validate;
}

@Override
public void setValidate(final boolean validate) {
this.validate = validate;
}

@Override
public boolean isSkipInvalidElements() {
return skipInvalidElements;
}

@Override
public void setSkipInvalidElements(final boolean skipInvalidElements) {
this.skipInvalidElements = skipInvalidElements;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "class")
@Override
public Object[] createInputArray() {
return MultiInput.super.createInputArray();
}

@Override
public Iterable<? extends Element> getInput() {
return elements;
}

@Override
public void setInput(final Iterable<? extends Element> elements) {
this.elements = elements;
}

@Override
public Map<String, String> getOptions() {
return options;
}

@Override
public void setOptions(final Map<String, String> options) {
this.options = options;
}

@Override
public DeleteElements shallowClone() {
return new DeleteElements.Builder()
.validate(validate)
.skipInvalidElements(skipInvalidElements)
.input(elements)
.options(options)
.build();
}

@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;

Check warning on line 118 in core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java

View check run for this annotation

Codecov / codecov/patch

core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java#L118

Added line #L118 was not covered by tests
}

if (null == obj || getClass() != obj.getClass()) {
return false;

Check warning on line 122 in core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java

View check run for this annotation

Codecov / codecov/patch

core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java#L122

Added line #L122 was not covered by tests
}

final DeleteElements deleteElements = (DeleteElements) obj;

Check warning on line 125 in core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java

View check run for this annotation

Codecov / codecov/patch

core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java#L125

Added line #L125 was not covered by tests

return new EqualsBuilder()
.append(options, deleteElements.options)
.append(validate, deleteElements.validate)
.append(skipInvalidElements, deleteElements.skipInvalidElements)
.append(elements, deleteElements.elements)
.isEquals();

Check warning on line 132 in core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java

View check run for this annotation

Codecov / codecov/patch

core/operation/src/main/java/uk/gov/gchq/gaffer/operation/impl/delete/DeleteElements.java#L127-L132

Added lines #L127 - L132 were not covered by tests
}

@Override
public int hashCode() {
return new HashCodeBuilder(67, 23)
.append(options)
.append(validate)
.append(skipInvalidElements)
.append(elements)
.toHashCode();
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("options", options)
.append("validate", validate)
.append("skipInvalidElements", skipInvalidElements)
.append("elements", elements)
.toString();
}

public static class Builder extends Operation.BaseBuilder<DeleteElements, Builder>
implements Validatable.Builder<DeleteElements, Builder>,
MultiInput.Builder<DeleteElements, Element, Builder> {
public Builder() {
super(new DeleteElements());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2024 Crown Copyright
*
* Licensed 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 uk.gov.gchq.gaffer.operation.impl.delete;

import org.junit.jupiter.api.Test;

import uk.gov.gchq.gaffer.commonutil.JsonAssert;
import uk.gov.gchq.gaffer.exception.SerialisationException;
import uk.gov.gchq.gaffer.jsonserialisation.JSONSerialiser;
import uk.gov.gchq.gaffer.operation.OperationTest;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

public class DeleteElementsTest extends OperationTest<DeleteElements> {

public static final String DELETE_ELEMENTS_JSON = String.format("{%n" +
" \"class\" : \"uk.gov.gchq.gaffer.operation.impl.delete.DeleteElements\",%n" +
" \"skipInvalidElements\" : false,%n" +
" \"validate\" : true%n" +
"}");

@Test
@Override
public void shouldShallowCloneOperation() {
// Given
final DeleteElements deleteElements = new DeleteElements.Builder()
.validate(false)
.skipInvalidElements(false)
.option("testOption", "true")
.build();

// When
final DeleteElements clone = deleteElements.shallowClone();

// Then
assertThat(clone).isNotSameAs(deleteElements);
assertThat(clone.isValidate()).isFalse();
assertThat(clone.isSkipInvalidElements()).isFalse();
assertThat(clone.getOption("testOption")).isEqualTo("true");
}

@Test
void shouldJSONSerialiseAndDeserialise() throws SerialisationException {
// Given
final DeleteElements deleteElements = getTestObject();

final Map<String, String> options = new HashMap<>();
options.put("option", "value");

deleteElements.setOptions(options);

// When
String json = new String(JSONSerialiser.serialise(deleteElements, true));

// Then
JsonAssert.assertEquals(String.format("{%n" +
" \"class\" : \"uk.gov.gchq.gaffer.operation.impl.delete.DeleteElements\",%n" +
" \"validate\" : true,%n" +
" \"options\" : {\"option\": \"value\"},%n" +
" \"skipInvalidElements\" : false%n" +
"}"), json);
}

@Test
void shouldSerialiseDeleteElementsOperation() throws IOException {
// Given
final DeleteElements deleteElements = new DeleteElements.Builder().build();

// When
String json = new String(JSONSerialiser.serialise(deleteElements, true));

// Then
JsonAssert.assertEquals(DELETE_ELEMENTS_JSON, json);
}

@Test
@Override
public void builderShouldCreatePopulatedOperation() {
// Given/When
final DeleteElements deleteElements = new DeleteElements.Builder()
.skipInvalidElements(true)
.option("testOption", "true")
.validate(false)
.build();

// Then
assertThat(deleteElements.getOption("testOption")).isEqualTo("true");
assertThat(deleteElements.isSkipInvalidElements()).isTrue();
assertThat(deleteElements.isValidate()).isFalse();
}

@Test
void shouldGetDeleteElementsObjectToString() {
// Given
final DeleteElements deleteElements = getTestObject();

// When/Then
assertThat(deleteElements).hasToString("DeleteElements[validate=true,skipInvalidElements=false]");
}

@Override
protected DeleteElements getTestObject() {
return new DeleteElements();
}
}
Loading