Description
I want to query the data in our Elastic Cloud instance using the REST API with authorization via API Key.
Steps taken
I tried using the SQL API as well as the Search API. NB that I would much preferably use the SQL API.
Based on the following curl commands provided in the documentation:
curl -X POST "localhost:9200/_sql?format=txt&pretty" -H 'Content-Type: application/json' -d'
{
"query": "SELECT * FROM library WHERE release_date < \u00272000-01-01\u0027"
}
'
[source]
curl -u elastic:password https://CLUSTER_ID.REGION.PLATFORM.found.io:9243/my_index/my_type -XPOST -d '{
"title": "One", "tags": ["ruby"]
}'
{"_index":"my_index","_type":"my_type","_id":"AV3ZeXsOMOVbmlCACuwj","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true}
[source]
and the documentation about the REST API, I attempted the following:
import base64
import json
import requests
if __name__ == '__main__':
response1 = requests.post(
'https://{redacted-id}.northamerica-northeast1.gcp.elastic-cloud.com:9243/_sql?format=txt',
headers={
"Authorization": base64.standard_b64encode(bytes('{API_KEY_ID}:{API_KEY_KEY}', 'utf-8')),
"Content-Type": 'application/json'
},
data={
"query": """
...
"""
}
)
print('response1')
print(response1)
response2 = requests.get(
'https://{redacted-id}.northamerica-northeast1.gcp.elastic-cloud.com:9243/logs-pubsub/_search',
headers={
"Authorization": base64.standard_b64encode(bytes('{API_KEY_ID}:{API_KEY_KEY}', 'utf-8')),
"Content-Type": 'application/json'
},
data={
"query": {
# ...
}
}
)
print('response2')
print(response2)
But both queries answer with 404 - Not Found.
What did I miss? Am I missing a part of the path like /api/..., /rest/...? Or is this a misdirection from a 403 to a 404 and the issue is the API Key?
Thanks!
The kind folks at ElasticStack provided me with the following answer. There were two issues:
The URL above was pointing to the Kibana instance, not to the ElasticCloud instance
In order to get the appropriate URL:
Navigate to https://cloud.elastic.co/deployments/
Click on your deployment:
Click on Copy Endpoint for the Elasticsearch endpoint:
The Authorization header was malformed
The Authorization header for an ApiKey powered request is the following:
ApiKey {B64_ENCODE('API_KEY_ID:API_KEY_KEY')}
Which can be written in python as:
"ApiKey " + str(base64.standard_b64encode(bytes('API_KEY_ID:API_KEY_KEY', 'utf-8')), 'utf-8')
On a final note, the team also strongly suggested I look at their Free on-demand Elasticsearch training.
Hopefully this will be helpful to whoever passes by here!
Related
I have been experimenting with Content Negotiation as backend versioning for my SpringBoot/Kotlin application. I have the following:
#GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE])
fun getUsers() {
//some code here
}
I have found this project combining accept" header and a "Accept-Version" custom header. I wonder whether this is the correct way of implementing a content negotiation approach and if not how can I fix it?
#GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE], headers = ["Accept-Version=$CUSTOM_ACCEPT_HEADER"])
fun getUsers() {
//some code here
}
object VersioningUtility {
const val CUSTOM_ACCEPT_HEADER = "vnd.sample.com-v1+json"
//here more constants as each controller can be versioned independently
}
Thank you
Yes, you can implement API versioning using content negotiation by having a custom header and header value as you have specified. However, since that is not a standard header, there are other scenarios which you might have to handle by yourself, such as:
default representation when the header is not present
exception scenarios when invalid media type values are passed as part of the header.
In case you are working with only json responses, the JSON API standard for content negotiation is to send the Accept header with the value application/vnd.api+json. Since Accept is a standard request header, using that is preferred. In case you need to handle other types of responses, you can still go ahead with the custom header.
You can implement content negotiation as below:
#RestController
class UserController {
#GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_1_HEADER}"])
fun getUser(): ResponseEntity<Any> {
return ResponseEntity(listOf(User("Abraham Lincoln")), HttpStatus.OK)
}
#GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_2_HEADER}"])
fun getNewUser(): ResponseEntity<Any> {
return ResponseEntity(listOf(NewUser(Name("Abraham", "Lincoln"))), HttpStatus.OK)
}
}
data class User(val name: String)
data class NewUser(val name: Name)
data class Name(val firstName: String, val lastName: String)
object VersioningUtility {
const val VERSION_1_HEADER = "application/vnd.v1+json"
const val VERSION_2_HEADER = "application/vnd.v2+json"
}
The above with enable you to have 2 versions of the GET /users endpoint with the Accept header.
When the curl request is made with v1 of the header value, the response would be according to the version v1
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v1+json'
[
{
"name": "Abraham Lincoln"
}
]
When the curl request is made with v2 of the header value, the response would be according to the version v2
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v2+json'
[
{
"name": {
"firstName": "Abraham",
"lastName": "Lincoln"
}
}
]
When an invalid header value is sent, it would respond with a 406 Not Acceptable
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.abc+json'
{
"timestamp": "2020-04-01T18:33:16.393+0000",
"status": 406,
"error": "Not Acceptable",
"message": "Could not find acceptable representation",
"path": "/users"
}
When no Accept header is sent, it would respond with the default version, ie v1 here
curl -L -X GET 'http://localhost:8080/users'
[
{
"name": "Abraham Lincoln"
}
]
Even GitHub has implemented versioning with content negotiation in a similar way and you can have a look at that in their documentation.
I am unable to retrieve all the fields, which i post for ProcedureRequest to a FHIR database.
Step 1: Post Request
curl -X POST https://fhir.dstu2.safetylabs.org/api/smartdstu2/open/ProcedureRequest \
-H 'Content-type: application/json+fhir' \
--data #ProcedureRequest.js
Result of POST
{
"resourceType":"OperationOutcome",
"text":{
"status":"generated",
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\"><h1>Operation Outcome</h1><table border=\"0\"><tr><td style=\"font-weight: bold;\">information</td><td>[]</td><td><pre>Successfully created resource "ProcedureRequest/7660/_history/1" in 7ms</pre></td>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</table>\n\t</div>"
},
"issue":[
{
"severity":"information",
"code":"informational",
"diagnostics":"Successfully created resource \"ProcedureRequest/7660/_history/1\" in 7ms"
}
]
}
This request was successful.
The JSON was validated from JSON validator, and It is also validated from hapi test Server from given link
" https://fhirtest.uhn.ca/resource?serverId=home_21&pretty=false&resource=ProcedureRequest"
Step 2: Retrieve Request
curl -X GET https://fhir.dstu2.safetylabs.org/api/smartdstu2/open/ProcedureRequest/7660
Results of GET
{
"resourceType":"ProcedureRequest",
"id":"7660",
"meta":{
"versionId":"1",
"lastUpdated":"2017-11-18T13:49:23.000+00:00"
},
"text":{
"status":"generated",
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: subrequest</p><p><b>definition</b>: Protocol for alergies</p><p><b>basedOn</b>: Original Request</p><p><b>replaces</b>: Previous allergy test</p><p><b>requisition</b>: A13848392</p><p><b>status</b>: active</p><p><b>intent</b>: instance-order</p><p><b>priority</b>: routine</p><p><b>code</b>: Peanut IgG <span>(Details : {LOINC code '35542-0' = 'Peanut IgG Ab [Mass/volume] in Serum)</span></p><p><b>subject</b>: <a>Patient/dicom</a></p><p><b>occurrence</b>: 08/05/2013 9:33:27 AM</p><h3>Requesters</h3><table><tr><td>-</td><td><b>Agent</b></td></tr><tr><td>*</td><td><a>Dr. Adam Careful</a></td></tr></table><p><b>performerType</b>: Nurse <span>(Details : {[not stated] code 'null' = 'null', given as 'Qualified nurse'})</span></p><p><b>reasonCode</b>: Check for Peanut Allergy <span>(Details )</span></p><p><b>bodySite</b>: Right arm <span>(Details : {[not stated] code 'null' = 'null', given as 'Right arm'})</span></p></div>"
},
"subject":{
"reference":"Patient/SL88812358"
},
"code":{
"coding":[
{
"system":"http://loinc.org",
"code":"35542-0"
}
],
"text":"Peanut IgG"
},
"bodySite":[
{
"coding":[
{
"display":"Right arm"
}
],
"text":"Right arm"
}
],
"status":"active",
"priority":"routine"
}
Note that Retrieve did not retrieve the following fields
occurrenceTiming
occurrenceDateTime
performerType
reasonCode
requester
Question : How do I request all the fields I posted?
That's because you posted to a DSTU2 server and those elements don't exist in DSTU2 - so the server ignored them (as servers are allowed to do with unrecognized elements). If you post to a DSTU3 server, the elements should be stored - if the server supports them, which most test servers do.
I am trying to create a chatbot in DialogueFlow. In the docs it says
You can create your own entities for your agents, either through web forms, uploading them in JSON or CSV formats, or via API calls.
How do I create an entity using an API call?
Send a POST request! Dialogflow has good REST endpoints.
curl -X POST \
'https://api.dialogflow.com/v1/entities?v=20150910' \
-H 'Authorization: Bearer YOUR_DEVELOPER_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data '{
"entries": [{
"synonyms": ["apple", "red apple"],
"value": "apple"
},
{
"value": "banana"
}
],
"name": "fruit"
}'
From the docs.
this is exactly what I was looking for.
But I've just spent a couple of hours googling trying to discover how can I send this curl POST and unfortunately I didn't find nothing that can help me.
If someone can give a light here I be very happy.
Some details:
I communicate with my chatbot throw a python Flask server, this means that I am using the python SDK.
In which part of the code in the server should I make this request?
Here is the solution that I found:
import os.path
import sys
import requests
import json
DEVELOPER_ACCESS_TOKEN = 'your developer token'
def sending_entities():
# 1 DEFINE THE URL
url = 'https://api.dialogflow.com/v1/entities?v=20150910'
# 2 DEFINE THE HEADERS
headers = {'Authorization': 'Bearer '+DEVELOPER_ACCESS_TOKEN,'Content-Type': 'application/json'}
# 3 CREATE THE DATA
data = json.dumps({
"name": "fruit",
"entries": [
{
"synonyms": ["apple", "red apple"],
"value": "apple"
},
{
"value": "banana"
}
]
})
# 4 MAKE THE REQUEST
response = requests.post(url,headers=headers,data=data)
print (response.json)
I'm trying to send this request using Java API:
curl -XPUT 'http://localhost:9201/living/_alias/living_team' -d '
{
"routing": "living_team",
"filter": {
"term": {
"user": "living_team" //user property must exists
}
}
}'
Up to now, I've not been able to figure out how exactly build the Java request:
this.elasticsearchResources.getElasticsearchClient()
.admin()
.indices()
.prepareAliases()
.addAlias(
ElasticsearchRepository.ELASTICSEARCH_INDEX,
alias,
QueryBuilders.termQuery("user", alias);
This line only creates an ADD ALIAS request with a filter, nevertheless I don't know how to set routing path...
How could I set the routing path up on request?
You mean how to set "http://localhost:9201/living/_alias/living_team" on your request?
PS. I wrote this as an answer cause i cannot comment yet.
Edited:
Hope this can help you
IndicesAliasesRequest request = new IndicesAliasesRequest();
request.addAliasAction(new AliasAction(AliasAction.Type.ADD).alias("the_alias").index(index).searchRouting("the_search_routing").indexRouting("the_index_routing"));
IndicesAliasesResponse response = elasticsearchClient.admin().indices().aliases(request).get();
I insert data to Elasticsearch with id 123
localhost:9200/index/type/123
but I do not know what will next id inserted
how insert data to Elasticsearch without id in localhost:9200/index/type?
The index operation can be executed without specifying the id. In such a case, an id will be generated automatically. In addition, the op_type will automatically be set to create. Here is an example (note the POST used instead of PUT):
$ curl -XPOST 'http://localhost:9200/twitter/tweet/' -d '{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}'
In my case, using nodejs and the elasticsearch package I did it this way using the client:
client.index ()
var elasticsearch = require ('elasticsearch');
let client = new elasticsearch.Client ({
host: '127.0.0.1: 9200'
});
client.index ({
index: 'myindex'
type: 'mytype',
body: {
properti1: 'val 1',
properti2: ['y', 'z'],
properti3: true,
}
}, function (error, response) {
if (error) {
console.log("error: ", error);
} else {
console.log("response: ", response);
}
});
if an id is not specified, elasticsearch will generate one automatically
In my case, I was trying to add a document directly to an index, e.g. localhost:9200/messages, as opposed to localhost:9200/someIndex/messages.
I had to append /_doc to the URL for my POST to succeed: localhost:9200/messages/_doc. Otherwise, I was getting an HTTP 405:
{"error":"Incorrect HTTP method for uri [/messages] and method [POST], allowed: [GET, PUT, HEAD, DELETE]","status":405}
Here's my full cURL request:
$ curl -X POST "localhost:9200/messages/_doc" -H 'Content-Type:
application/json' -d'
{
"user": "Jimmy Doe",
"text": "Actually, my only brother!",
"timestamp": "something"
}
'
{"_index":"messages","_type":"_doc","_id":"AIRF8GYBjAnm5hquWm61","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":2,"_primary_term":3}
You can use POST request to create a new document or data object without specifying id property in the path.
curl -XPOST 'http://localhost:9200/stackoverflow/question' -d '
{
title: "How to insert data to elasticsearch without id in the path?"
}
If our data doesn’t have a natural ID, we can let Elasticsearch autogenerate one for us. The structure of the request changes: instead of using the PUT verb ("store this document at this URL"), we use the POST verb ("store this document under this URL").
The URL now contains just the _index and the _type:
curl -X POST "localhost:9200/website/blog/" -H 'Content-Type: application/json' -d'
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
'
The response is similar to what we saw before, except that the _id field has been generated for us:
{
"_index": "website",
"_type": "blog",
"_id": "AVFgSgVHUP18jI2wRx0w",
"_version": 1,
"created": true
}
Autogenerated IDs are 20 character long, URL-safe, Base64-encoded GUID strings. These GUIDs are generated from a modified FlakeID scheme which allows multiple nodes to be generating unique IDs in parallel with essentially zero chance of collision.
https://www.elastic.co/guide/en/elasticsearch/guide/current/index-doc.html
It's possible to leave the ID field blank and elasticsearch will assign it one. For example a _bulk insert will look like
{"create":{"_index":"products","_type":"product"}}\n
{JSON document 1}\n
{"create":{"_index":"products","_type":"product"}}\n
{JSON document 2}\n
{"create":{"_index":"products","_type":"product"}}\n
{JSON document 3}\n
...and so on
The IDs will look something like 'AUvGyJMOOA8IPUB04vbF'