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

[Connector API] Implement _features endpoint #109248

Merged
merged 4 commits into from
Jun 4, 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
@@ -0,0 +1,38 @@
{
"connector.update_features": {
"documentation": {
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/update-connector-features-api.html",
"description": "Updates the connector features in the connector document."
},
"stability": "experimental",
"visibility": "public",
"headers": {
"accept": [
"application/json"
],
"content_type": [
"application/json"
]
},
"url": {
"paths": [
{
"path": "/_connector/{connector_id}/_features",
"methods": [
"PUT"
],
"parts": {
"connector_id": {
"type": "string",
"description": "The unique identifier of the connector to be updated."
}
}
}
]
},
"body": {
"description": "An object containing the connector's features definition.",
"required": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
setup:
- requires:
cluster_features: ["gte_v8.15.0"]
reason: Introduced in 8.15.0

- do:
connector.put:
connector_id: test-connector
body:
index_name: search-1-test
name: my-connector
language: pl
is_native: false
service_type: super-connector

---
"Update Connector Features":
- do:
connector.update_features:
connector_id: test-connector
body:
features:
document_level_security: { enabled: true }
native_connector_api_keys: { enabled: true }
incremental_sync: { enabled: false }
sync_rules:
basic: { enabled: true }
advanced: { enabled: false }


- match: { result: updated }

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

- match: { features.document_level_security.enabled: true }
- match: { features.native_connector_api_keys.enabled: true }
- match: { features.incremental_sync.enabled: false }
- match: { features.sync_rules.basic.enabled: true }
- match: { features.sync_rules.advanced.enabled: false }

---
"Update Connector Features - Partial Update":
- do:
connector.update_features:
connector_id: test-connector
body:
features:
document_level_security: { enabled: true }


- match: { result: updated }

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

- match: { features.document_level_security.enabled: true }


- do:
connector.update_features:
connector_id: test-connector
body:
features:
native_connector_api_keys: { enabled: true }


- match: { result: updated }

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

# Assert that existing feature remains unchanged
- match: { features.document_level_security.enabled: true }
- match: { features.native_connector_api_keys.enabled: true }

---
"Update Connector Features - 404 when connector doesn't exist":
- do:
catch: "missing"
connector.update_features:
connector_id: test-non-existent-connector
body:
features:
native_connector_api_keys: { enabled: true }

---
"Update Connector Features - 400 status code when connector_id is empty":
- do:
catch: "bad_request"
connector.update_features:
connector_id: ""
body:
features:
native_connector_api_keys: { enabled: true }

---
"Update Connector Features - 400 status code when payload unknown":
- do:
catch: "bad_request"
connector.update_features:
connector_id: test-connector
body:
featuresss:
not_a_feature: 12423
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorApiKeyIdAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorConfigurationAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorErrorAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorFeaturesAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorFilteringAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorFilteringValidationAction;
import org.elasticsearch.xpack.application.connector.action.RestUpdateConnectorIndexNameAction;
Expand All @@ -78,6 +79,7 @@
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorApiKeyIdAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorConfigurationAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorErrorAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorFeaturesAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorFilteringAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorFilteringValidationAction;
import org.elasticsearch.xpack.application.connector.action.TransportUpdateConnectorIndexNameAction;
Expand All @@ -93,6 +95,7 @@
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorApiKeyIdAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorConfigurationAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorErrorAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorFeaturesAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorFilteringAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorFilteringValidationAction;
import org.elasticsearch.xpack.application.connector.action.UpdateConnectorIndexNameAction;
Expand Down Expand Up @@ -267,6 +270,7 @@ protected XPackLicenseState getLicenseState() {
new ActionHandler<>(UpdateConnectorApiKeyIdAction.INSTANCE, TransportUpdateConnectorApiKeyIdAction.class),
new ActionHandler<>(UpdateConnectorConfigurationAction.INSTANCE, TransportUpdateConnectorConfigurationAction.class),
new ActionHandler<>(UpdateConnectorErrorAction.INSTANCE, TransportUpdateConnectorErrorAction.class),
new ActionHandler<>(UpdateConnectorFeaturesAction.INSTANCE, TransportUpdateConnectorFeaturesAction.class),
new ActionHandler<>(UpdateConnectorFilteringAction.INSTANCE, TransportUpdateConnectorFilteringAction.class),
new ActionHandler<>(UpdateConnectorActiveFilteringAction.INSTANCE, TransportUpdateConnectorActiveFilteringAction.class),
new ActionHandler<>(
Expand Down Expand Up @@ -368,6 +372,7 @@ public List<RestHandler> getRestHandlers(
new RestUpdateConnectorConfigurationAction(),
new RestUpdateConnectorErrorAction(),
new RestUpdateConnectorActiveFilteringAction(),
new RestUpdateConnectorFeaturesAction(),
new RestUpdateConnectorFilteringValidationAction(),
new RestUpdateConnectorFilteringAction(),
new RestUpdateConnectorIndexNameAction(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public Connector(StreamInput in) throws IOException {
static final ParseField CUSTOM_SCHEDULING_FIELD = new ParseField("custom_scheduling");
public static final ParseField DESCRIPTION_FIELD = new ParseField("description");
public static final ParseField ERROR_FIELD = new ParseField("error");
static final ParseField FEATURES_FIELD = new ParseField("features");
public static final ParseField FEATURES_FIELD = new ParseField("features");
public static final ParseField FILTERING_FIELD = new ParseField("filtering");
public static final ParseField INDEX_NAME_FIELD = new ParseField("index_name");
public static final ParseField IS_NATIVE_FIELD = new ParseField("is_native");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,22 @@ public static ConnectorFeatures fromXContentBytes(BytesReference source, XConten
}
}

public FeatureEnabled getDocumentLevelSecurityEnabled() {
return documentLevelSecurityEnabled;
}

public FeatureEnabled getIncrementalSyncEnabled() {
return incrementalSyncEnabled;
}

public FeatureEnabled getNativeConnectorAPIKeysEnabled() {
return nativeConnectorAPIKeysEnabled;
}

public SyncRulesFeatures getSyncRulesFeatures() {
return syncRulesFeatures;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,33 @@ public void updateConnectorFiltering(String connectorId, List<ConnectorFiltering
}
}

/**
* Updates the features of a given {@link Connector}.
*
* @param connectorId The ID of the {@link Connector} to be updated.
* @param features An instance of {@link ConnectorFeatures}
* @param listener Listener to respond to a successful response or an error.
*/
public void updateConnectorFeatures(String connectorId, ConnectorFeatures features, ActionListener<UpdateResponse> listener) {
try {
final UpdateRequest updateRequest = new UpdateRequest(CONNECTOR_INDEX_NAME, connectorId).doc(
new IndexRequest(CONNECTOR_INDEX_NAME).opType(DocWriteRequest.OpType.INDEX)
.id(connectorId)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.source(Map.of(Connector.FEATURES_FIELD.getPreferredName(), features))
);
client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
return;
}
l.onResponse(updateResponse);
}));
} catch (Exception e) {
listener.onFailure(e);
}
}

/**
* Updates the draft filtering in a given {@link Connector}.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.application.connector.action;

import org.elasticsearch.client.internal.node.NodeClient;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.Scope;
import org.elasticsearch.rest.ServerlessScope;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.application.EnterpriseSearch;

import java.util.List;

import static org.elasticsearch.rest.RestRequest.Method.PUT;

@ServerlessScope(Scope.PUBLIC)
public class RestUpdateConnectorFeaturesAction extends BaseRestHandler {

@Override
public String getName() {
return "connector_update_features_action";
}

@Override
public List<Route> routes() {
return List.of(new Route(PUT, "/" + EnterpriseSearch.CONNECTOR_API_ENDPOINT + "/{connector_id}/_features"));
}

@Override
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) {
UpdateConnectorFeaturesAction.Request request = UpdateConnectorFeaturesAction.Request.fromXContentBytes(
restRequest.param("connector_id"),
restRequest.content(),
restRequest.getXContentType()
);
return channel -> client.execute(
UpdateConnectorFeaturesAction.INSTANCE,
request,
new RestToXContentListener<>(channel, ConnectorUpdateActionResponse::status)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.application.connector.action;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.application.connector.ConnectorIndexService;

public class TransportUpdateConnectorFeaturesAction extends HandledTransportAction<
UpdateConnectorFeaturesAction.Request,
ConnectorUpdateActionResponse> {

protected final ConnectorIndexService connectorIndexService;

@Inject
public TransportUpdateConnectorFeaturesAction(TransportService transportService, ActionFilters actionFilters, Client client) {
super(
UpdateConnectorFeaturesAction.NAME,
transportService,
actionFilters,
UpdateConnectorFeaturesAction.Request::new,
EsExecutors.DIRECT_EXECUTOR_SERVICE
);
this.connectorIndexService = new ConnectorIndexService(client);
}

@Override
protected void doExecute(
Task task,
UpdateConnectorFeaturesAction.Request request,
ActionListener<ConnectorUpdateActionResponse> listener
) {
connectorIndexService.updateConnectorFeatures(
request.getConnectorId(),
request.getFeatures(),
listener.map(r -> new ConnectorUpdateActionResponse(r.getResult()))
);
}
}
Loading