|  | @@ -0,0 +1,191 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 | 
	
		
			
				|  |  | + * or more contributor license agreements. Licensed under the Elastic License
 | 
	
		
			
				|  |  | + * 2.0; you may not use this file except in compliance with the Elastic License
 | 
	
		
			
				|  |  | + * 2.0.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +package org.elasticsearch.xpack.eql.action;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import org.elasticsearch.ExceptionsHelper;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.ActionResponse;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.io.stream.StreamInput;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.io.stream.StreamOutput;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.xcontent.StatusToXContentObject;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.xcontent.XContentBuilder;
 | 
	
		
			
				|  |  | +import org.elasticsearch.rest.RestStatus;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xpack.core.search.action.SearchStatusResponse;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xpack.eql.async.StoredAsyncResponse;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.io.IOException;
 | 
	
		
			
				|  |  | +import java.util.Objects;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import static org.elasticsearch.rest.RestStatus.OK;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * A response for eql search status request
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +public class EqlStatusResponse extends ActionResponse implements SearchStatusResponse, StatusToXContentObject {
 | 
	
		
			
				|  |  | +    private final String id;
 | 
	
		
			
				|  |  | +    private final boolean isRunning;
 | 
	
		
			
				|  |  | +    private final boolean isPartial;
 | 
	
		
			
				|  |  | +    private final Long startTimeMillis;
 | 
	
		
			
				|  |  | +    private final long expirationTimeMillis;
 | 
	
		
			
				|  |  | +    private final RestStatus completionStatus;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public EqlStatusResponse(String id,
 | 
	
		
			
				|  |  | +            boolean isRunning,
 | 
	
		
			
				|  |  | +            boolean isPartial,
 | 
	
		
			
				|  |  | +            Long startTimeMillis,
 | 
	
		
			
				|  |  | +            long expirationTimeMillis,
 | 
	
		
			
				|  |  | +            RestStatus completionStatus) {
 | 
	
		
			
				|  |  | +        this.id = id;
 | 
	
		
			
				|  |  | +        this.isRunning = isRunning;
 | 
	
		
			
				|  |  | +        this.isPartial = isPartial;
 | 
	
		
			
				|  |  | +        this.startTimeMillis = startTimeMillis;
 | 
	
		
			
				|  |  | +        this.expirationTimeMillis = expirationTimeMillis;
 | 
	
		
			
				|  |  | +        this.completionStatus = completionStatus;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Get status from the stored eql search response
 | 
	
		
			
				|  |  | +     * @param storedResponse
 | 
	
		
			
				|  |  | +     * @param expirationTimeMillis – expiration time in milliseconds
 | 
	
		
			
				|  |  | +     * @param id – encoded async search id
 | 
	
		
			
				|  |  | +     * @return a status response
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public static EqlStatusResponse getStatusFromStoredSearch(StoredAsyncResponse<EqlSearchResponse> storedResponse,
 | 
	
		
			
				|  |  | +            long expirationTimeMillis, String id) {
 | 
	
		
			
				|  |  | +        EqlSearchResponse searchResponse = storedResponse.getResponse();
 | 
	
		
			
				|  |  | +        if (searchResponse != null) {
 | 
	
		
			
				|  |  | +            assert searchResponse.isRunning() == false : "Stored eql search response must have a completed status!";
 | 
	
		
			
				|  |  | +            return new EqlStatusResponse(
 | 
	
		
			
				|  |  | +                searchResponse.id(),
 | 
	
		
			
				|  |  | +                false,
 | 
	
		
			
				|  |  | +                searchResponse.isPartial(),
 | 
	
		
			
				|  |  | +                null,  // we dont' store in the index start time for completed response
 | 
	
		
			
				|  |  | +                expirationTimeMillis,
 | 
	
		
			
				|  |  | +                RestStatus.OK
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            Exception exc = storedResponse.getException();
 | 
	
		
			
				|  |  | +            assert exc != null : "Stored eql response must either have a search response or an exception!";
 | 
	
		
			
				|  |  | +            return new EqlStatusResponse(
 | 
	
		
			
				|  |  | +                id,
 | 
	
		
			
				|  |  | +                false,
 | 
	
		
			
				|  |  | +                false,
 | 
	
		
			
				|  |  | +                null, // we dont' store in the index start time for completed response
 | 
	
		
			
				|  |  | +                expirationTimeMillis,
 | 
	
		
			
				|  |  | +                ExceptionsHelper.status(exc)
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public EqlStatusResponse(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +        this.id = in.readString();
 | 
	
		
			
				|  |  | +        this.isRunning = in.readBoolean();
 | 
	
		
			
				|  |  | +        this.isPartial = in.readBoolean();
 | 
	
		
			
				|  |  | +        this.startTimeMillis = in.readOptionalLong();
 | 
	
		
			
				|  |  | +        this.expirationTimeMillis = in.readLong();
 | 
	
		
			
				|  |  | +        this.completionStatus = (this.isRunning == false) ? RestStatus.readFrom(in) : null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void writeTo(StreamOutput out) throws IOException {
 | 
	
		
			
				|  |  | +        out.writeString(id);
 | 
	
		
			
				|  |  | +        out.writeBoolean(isRunning);
 | 
	
		
			
				|  |  | +        out.writeBoolean(isPartial);
 | 
	
		
			
				|  |  | +        out.writeOptionalLong(startTimeMillis);
 | 
	
		
			
				|  |  | +        out.writeLong(expirationTimeMillis);
 | 
	
		
			
				|  |  | +        if (isRunning == false) {
 | 
	
		
			
				|  |  | +            RestStatus.writeTo(out, completionStatus);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public RestStatus status() {
 | 
	
		
			
				|  |  | +        return OK;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
 | 
	
		
			
				|  |  | +        builder.startObject();
 | 
	
		
			
				|  |  | +        builder.field("id", id);
 | 
	
		
			
				|  |  | +        builder.field("is_running", isRunning);
 | 
	
		
			
				|  |  | +        builder.field("is_partial", isPartial);
 | 
	
		
			
				|  |  | +        if (startTimeMillis != null) { // start time is available only for a running eql search
 | 
	
		
			
				|  |  | +            builder.timeField("start_time_in_millis", "start_time", startTimeMillis);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        builder.timeField("expiration_time_in_millis", "expiration_time", expirationTimeMillis);
 | 
	
		
			
				|  |  | +        if (isRunning == false) { // completion status is available only for a completed eql search
 | 
	
		
			
				|  |  | +            builder.field("completion_status", completionStatus.getStatus());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        builder.endObject();
 | 
	
		
			
				|  |  | +        return builder;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public boolean equals(Object obj) {
 | 
	
		
			
				|  |  | +        if (this == obj) return true;
 | 
	
		
			
				|  |  | +        if (obj == null || getClass() != obj.getClass()) return false;
 | 
	
		
			
				|  |  | +        EqlStatusResponse other = (EqlStatusResponse) obj;
 | 
	
		
			
				|  |  | +        return id.equals(other.id)
 | 
	
		
			
				|  |  | +            && isRunning == other.isRunning
 | 
	
		
			
				|  |  | +            && isPartial == other.isPartial
 | 
	
		
			
				|  |  | +            && Objects.equals(startTimeMillis, other.startTimeMillis)
 | 
	
		
			
				|  |  | +            && expirationTimeMillis == other.expirationTimeMillis
 | 
	
		
			
				|  |  | +            && Objects.equals(completionStatus, other.completionStatus);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public int hashCode() {
 | 
	
		
			
				|  |  | +        return Objects.hash(id, isRunning, isPartial, startTimeMillis, expirationTimeMillis, completionStatus);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Returns the id of the eql search status request.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public String getId() {
 | 
	
		
			
				|  |  | +        return id;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Returns {@code true} if the eql search is still running in the cluster,
 | 
	
		
			
				|  |  | +     * or {@code false} if the search has been completed.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public boolean isRunning() {
 | 
	
		
			
				|  |  | +        return isRunning;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Returns {@code true} if the eql search results are partial.
 | 
	
		
			
				|  |  | +     * This could be either because eql search hasn't finished yet,
 | 
	
		
			
				|  |  | +     * or if it finished and some shards have failed or timed out.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public boolean isPartial() {
 | 
	
		
			
				|  |  | +        return isPartial;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Returns a timestamp when the eql search task started, in milliseconds since epoch.
 | 
	
		
			
				|  |  | +     * For a completed eql search returns {@code null}, as we don't store start time for completed searches.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public Long getStartTime() {
 | 
	
		
			
				|  |  | +        return startTimeMillis;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Returns a timestamp when the eql search will be expired, in milliseconds since epoch.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public long getExpirationTime() {
 | 
	
		
			
				|  |  | +        return expirationTimeMillis;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * For a completed eql search returns the completion status.
 | 
	
		
			
				|  |  | +     * For a still running eql search returns {@code null}.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public RestStatus getCompletionStatus() {
 | 
	
		
			
				|  |  | +        return completionStatus;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |