Skip to content

Commit

Permalink
[Connector API] Support detached index for connectors (#106236)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedrazb committed Mar 14, 2024
1 parent 1b8f2cf commit d1cf3b1
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ setup:
body:
index_name: search-test

---
'Create Connector - Without index attached':
- do:
connector.put:
connector_id: test-connector-1
body:
service_type: super-connector

- match: { result: 'created' }

- do:
connector.get:
connector_id: test-connector-1

- match: { index_name: null }
- match: { service_type: super-connector }

---
"Put connector fails for unprivileged user":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ setup:
index_name: search-test


---
'Create Connector - Without index attached':
- do:
connector.post:
body:
service_type: super-connector

- set: { id: id }
- match: { id: $id }

- do:
connector.get:
connector_id: $id

- match: { index_name: null }
- match: { service_type: super-connector }

---
"Post connector fails for unprivileged user":
- skip:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ setup:
- match: { index_name: search-2-test }
- match: { status: created }

---
"Update Connector Index Name - detach index":
- do:
connector.update_index_name:
connector_id: test-connector
body:
index_name: null


- match: { result: updated }

- do:
connector.get:
connector_id: test-connector

- match: { index_name: null }

---
"Update Connector Index Name - 404 when connector doesn't exist":
- do:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ setup:
language: de
is_native: false
service_type: super-connector
- do:
connector.put:
connector_id: test-connector-detached-index
body:
name: my-connector
language: de
is_native: false
service_type: super-connector

---
'Create connector sync job':
Expand Down Expand Up @@ -289,6 +297,16 @@ setup:
catch: bad_request


---
'Create connector sync job with no index attached':
- do:
connector_sync_job.post:
body:
id: test-connector-detached-index
job_type: full
trigger_method: full
catch: bad_request

---
"Create connector sync job fails for unprivileged user":
- skip:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -107,7 +108,6 @@ public void createConnectorWithDocId(PutConnectorAction.Request request, ActionL
try {
isDataIndexNameAlreadyInUse(indexName, connectorId, listener.delegateFailure((l, isIndexNameInUse) -> {
if (isIndexNameInUse) {

l.onFailure(
new ElasticsearchStatusException(
"Index name [" + indexName + "] is used by another connector.",
Expand Down Expand Up @@ -188,7 +188,7 @@ public void createConnectorWithAutoGeneratedId(
* Creates a Connector with default values and specified parameters.
*
* @param description The description of the connector.
* @param indexName The name of the index associated with the connector.
* @param indexName The name of the index associated with the connector. It can be null to indicate that index is not attached yet.
* @param isNative Flag indicating if the connector is native; defaults to false if null.
* @param language The language supported by the connector.
* @param name The name of the connector; defaults to an empty string if null.
Expand Down Expand Up @@ -694,7 +694,8 @@ public void updateConnectorPipeline(UpdateConnectorPipelineAction.Request reques
}

/**
* Updates the index name property of a {@link Connector}.
* Updates the index name property of a {@link Connector}. Index name can be set to null to indicate that the connector
* is not associated with any index.
*
* @param request The request for updating the connector's index name.
* @param listener The listener for handling responses, including successful updates or errors.
Expand All @@ -720,8 +721,11 @@ public void updateConnectorIndexName(UpdateConnectorIndexNameAction.Request requ
new IndexRequest(CONNECTOR_INDEX_NAME).opType(DocWriteRequest.OpType.INDEX)
.id(connectorId)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.source(Map.of(Connector.INDEX_NAME_FIELD.getPreferredName(), request.getIndexName()))

.source(new HashMap<>() {
{
put(Connector.INDEX_NAME_FIELD.getPreferredName(), request.getIndexName());
}
})
);
client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (ll, updateResponse) -> {
if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
Expand Down Expand Up @@ -912,6 +916,10 @@ private static ConnectorSearchResult hitToConnector(SearchHit searchHit) {
* @param listener The listener for handling boolean responses and errors.
*/
private void isDataIndexNameAlreadyInUse(String indexName, String connectorId, ActionListener<Boolean> listener) {
if (indexName == null) {
listener.onResponse(false);
return;
}
try {
BoolQueryBuilder boolFilterQueryBuilder = new BoolQueryBuilder();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
package org.elasticsearch.xpack.application.connector.action;

import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.indices.InvalidIndexNameException;
import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;

import java.io.IOException;

import static org.elasticsearch.action.ValidateActions.addValidationError;

/**
* Abstract base class for action requests targeting the connectors index. Implements {@link org.elasticsearch.action.IndicesRequest}
* to ensure index-level privilege support. This class defines the connectors index as the target for all derived action requests.
Expand All @@ -29,6 +34,24 @@ public ConnectorActionRequest(StreamInput in) throws IOException {
super(in);
}

/**
* Validates the given index name and updates the validation exception if the name is invalid.
*
* @param indexName The index name to validate. If null, no validation is performed.
* @param validationException The exception to accumulate validation errors.
* @return The updated or original {@code validationException} with any new validation errors added, if the index name is invalid.
*/
public ActionRequestValidationException validateIndexName(String indexName, ActionRequestValidationException validationException) {
if (indexName != null) {
try {
MetadataCreateIndexService.validateIndexOrAliasName(indexName, InvalidIndexNameException::new);
} catch (InvalidIndexNameException e) {
return addValidationError(e.toString(), validationException);
}
}
return validationException;
}

@Override
public String[] indices() {
return new String[] { ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.indices.InvalidIndexNameException;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.ToXContentObject;
Expand All @@ -31,8 +28,6 @@
import java.io.IOException;
import java.util.Objects;

import static org.elasticsearch.action.ValidateActions.addValidationError;
import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;

public class PostConnectorAction {
Expand All @@ -46,6 +41,7 @@ public static class Request extends ConnectorActionRequest implements ToXContent

@Nullable
private final String description;
@Nullable
private final String indexName;
@Nullable
private final Boolean isNative;
Expand All @@ -68,7 +64,7 @@ public Request(String description, String indexName, Boolean isNative, String la
public Request(StreamInput in) throws IOException {
super(in);
this.description = in.readOptionalString();
this.indexName = in.readString();
this.indexName = in.readOptionalString();
this.isNative = in.readOptionalBoolean();
this.language = in.readOptionalString();
this.name = in.readOptionalString();
Expand All @@ -90,7 +86,7 @@ public Request(StreamInput in) throws IOException {

static {
PARSER.declareString(optionalConstructorArg(), new ParseField("description"));
PARSER.declareString(constructorArg(), new ParseField("index_name"));
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("index_name"));
PARSER.declareBoolean(optionalConstructorArg(), new ParseField("is_native"));
PARSER.declareString(optionalConstructorArg(), new ParseField("language"));
PARSER.declareString(optionalConstructorArg(), new ParseField("name"));
Expand All @@ -116,7 +112,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (description != null) {
builder.field("description", description);
}
builder.field("index_name", indexName);
if (indexName != null) {
builder.field("index_name", indexName);
}
if (isNative != null) {
builder.field("is_native", isNative);
}
Expand All @@ -138,14 +136,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;

if (Strings.isNullOrEmpty(getIndexName())) {
validationException = addValidationError("[index_name] cannot be [null] or [\"\"]", validationException);
}
try {
MetadataCreateIndexService.validateIndexOrAliasName(getIndexName(), InvalidIndexNameException::new);
} catch (InvalidIndexNameException e) {
validationException = addValidationError(e.toString(), validationException);
}
validationException = validateIndexName(indexName, validationException);

return validationException;
}
Expand All @@ -154,7 +145,7 @@ public ActionRequestValidationException validate() {
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalString(description);
out.writeString(indexName);
out.writeOptionalString(indexName);
out.writeOptionalBoolean(isNative);
out.writeOptionalString(language);
out.writeOptionalString(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.indices.InvalidIndexNameException;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ParseField;
Expand Down Expand Up @@ -49,6 +47,7 @@ public static class Request extends ConnectorActionRequest implements IndicesReq

@Nullable
private final String description;
@Nullable
private final String indexName;
@Nullable
private final Boolean isNative;
Expand Down Expand Up @@ -81,7 +80,7 @@ public Request(StreamInput in) throws IOException {
super(in);
this.connectorId = in.readString();
this.description = in.readOptionalString();
this.indexName = in.readString();
this.indexName = in.readOptionalString();
this.isNative = in.readOptionalBoolean();
this.language = in.readOptionalString();
this.name = in.readOptionalString();
Expand All @@ -104,7 +103,7 @@ public Request(StreamInput in) throws IOException {

static {
PARSER.declareString(optionalConstructorArg(), new ParseField("description"));
PARSER.declareString(optionalConstructorArg(), new ParseField("index_name"));
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("index_name"));
PARSER.declareBoolean(optionalConstructorArg(), new ParseField("is_native"));
PARSER.declareString(optionalConstructorArg(), new ParseField("language"));
PARSER.declareString(optionalConstructorArg(), new ParseField("name"));
Expand All @@ -130,7 +129,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (description != null) {
builder.field("description", description);
}
builder.field("index_name", indexName);
if (indexName != null) {
builder.field("index_name", indexName);
}
if (isNative != null) {
builder.field("is_native", isNative);
}
Expand All @@ -156,14 +157,8 @@ public ActionRequestValidationException validate() {
if (Strings.isNullOrEmpty(getConnectorId())) {
validationException = addValidationError("[connector_id] cannot be [null] or [\"\"]", validationException);
}
if (Strings.isNullOrEmpty(getIndexName())) {
validationException = addValidationError("[index_name] cannot be [null] or [\"\"]", validationException);
}
try {
MetadataCreateIndexService.validateIndexOrAliasName(getIndexName(), InvalidIndexNameException::new);
} catch (InvalidIndexNameException e) {
validationException = addValidationError(e.toString(), validationException);
}

validationException = validateIndexName(indexName, validationException);

return validationException;
}
Expand All @@ -173,7 +168,7 @@ public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(connectorId);
out.writeOptionalString(description);
out.writeString(indexName);
out.writeOptionalString(indexName);
out.writeOptionalBoolean(isNative);
out.writeOptionalString(language);
out.writeOptionalString(name);
Expand Down

0 comments on commit d1cf3b1

Please sign in to comment.