I have a working Flask API and now I want to implement search queries.
My understanding is that the filter is applied on the client and the Flask API takes care of it.
Flask==0.10.1
Flask-HTTPAuth==2.7.0
Flask-Limiter==0.9.1
Flask-Login==0.3.2
Flask-Mail==0.9.1
Flask-Principal==0.4.0
Flask-Restless==0.17.0
I have followed documentation and performed my search query, but still just retrieving same results:
http://flask-restless.readthedocs.org/en/latest/searchformat.html
No filter:
curl -u aaa:bbb -H "Content-Type: application/json" http://0.0.0.0:8080/api/1.0/job/
{
"jobs": [
{
"description": "ESXi job completed",
"reference": "07FC78BCC0",
"status": 1
},
{
"description": "Server discovery failed. Please verify HTTPS/SSH parameters",
"reference": "A6EE28F4C0",
"status": -1
}]
}
Search query based on:
http://flask-restless.readthedocs.org/en/latest/searchformat.html
curl -u aaa:bbb -G -H "Content-Type: application/json" -d '{
> "filters": [{"name": "description", "op": "like", "val": "%ESXi%"}]}' http://0.0.0.0:8080/api/1.0/job/
Or
curl -u aaa:bbb -G -H "Content-Type: application/json" -d '{"filters": [{"name": "status", "op": "eq", "val":0}]}' http://0.0.0.0:8080/api/1.0/job/
And same results are shown.
This is my Flask endpoint:
def get_jobs():
"""
:return:
"""
try:
log.info(request.remote_addr + ' ' + request.__repr__())
jobs = Model.Job.query.order_by(desc(Model.Job.job_start)).limit(settings.items_per_page).all()
# =========================================================
# Get JOBS
# =========================================================
values = ['description', 'status', 'reference']
response = [{value: getattr(d, value) for value in values} for d in jobs]
return jsonify(jobs=response)
except Exception, excpt:
log.exception(excpt.__repr__())
response = json.dumps('Internal Server Error. Please try again later')
resp = Response(response, status=500, mimetype='application/json')
return resp
My Model
class Job(db.Model, AutoSerialize, Serializer):
"""
"""
__tablename__ = 'job'
__public__ = ('status','description','reference','job_start','job_end')
id = Column(Integer, primary_key=True, server_default=text("nextval('job_id_seq'::regclass)"))
description = Column(String(200))
reference = Column(String(50))
job_start = Column(DateTime)
job_end = Column(DateTime)
fk_server = Column(ForeignKey(u'server.id'))
owner_id = Column(ForeignKey(u'auth_user.id'))
is_cluster = Column(Boolean)
host_information = Column(String(1024))
status = Column(Integer, nullable=False)
owner = relationship(u'AuthUser')
server = relationship(u'Server')
def serialize(self):
"""
:return:
"""
d = Serializer.serialize(self)
return d
Do I need to change anything?
Maybe having __public__ as a Job attribute is interfering with the way the filtering works. There's a warning in the Flask-Restless documentation about this.
Related
I am trying to run a curl command using the run command in the subprocess module in python. But it is failing.
However, the curl command for my elasticsearch search query API is working fine-
curl -k XGET -H "Content-Type: application/json" -H "Authorization: ApiKey Q2txZ2FvSUJ6Nlcwa3pjbnh0NUM6enBidjdNLTdRQVNhc0tuTEk5UEZmZw==" 'https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200/_search?index=.fleet-enrollment-api-keys-7&size=20&pretty' -d '{"query": {"match": {"_id": "c8a537a9-7f98-489b-8f8b-de2450c67ce6"}},"fields" : ["_id", "_index"]}'
This is the code that I am trying to run-
import json
from builtins import int
from pydantic import BaseModel, Field
from typing import List, Dict
from subprocess import PIPE, run
def elasticsearch_search_query(host: str,
port: int,
api_key: str,
path: str,
query: dict,
index: str,
size: int,
fields: List):
es_path = host + ":" + str(port) + path +"?index="+index+"&"+str(size)+"&pretty"
es_header = "Authorization: ApiKey" + " " + api_key
es_dict = {"query": query, "fields": fields}
es_json = json.dumps(es_dict)
es_replaced = " "+"'"+ es_json.replace("'", "\"")+"'"
result = run(["curl", "-k", "-XGET", "-H", "Content-Type: application/json", "-H",
es_header,
es_path,
"-d",
es_replaced]
# ,stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=False
)
print(es_path, es_header, es_replaced)
print(result.stdout)
elasticsearch_search_query(host="https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200/_search", api_key="Q2txZ2FvSUJ6Nlcwa3pjbnh0NUM6enBidjdNLTdRQVNhc0tuTEk5UEZmZw==",
port=9200, size=5, query={"match": {"_id": "c8a537a9-7f98-489b-8f8b-de2450c67ce6"}}, fields=["_id", "_index"], path="/_search",index=".fleet-enrollment-api-keys-7")
Here is the error-
{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "request [/_search:9200/_search] contains unrecognized parameter: [5]"
}
],
"type" : "illegal_argument_exception",
"reason" : "request [/_search:9200/_search] contains unrecognized parameter: [5]"
},
"status" : 400
}
https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200/_search:9200/_search?index=.fleet-enrollment-api-keys-7&5&pretty Authorization: ApiKey Q2txZ2FvSUJ6Nlcwa3pjbnh0NUM6enBidjdNLTdRQVNhc0tuTEk5UEZmZw== '{"query": {"match": {"_id": "c8a537a9-7f98-489b-8f8b-de2450c67ce6"}}, "fields": ["_id", "_index"]}'
None
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 350 100 350 0 0 10294 0 --:--:-- --:--:-- --:--:-- 10294
curl: (3) nested brace in URL position 12:
'{"query": {"match": {"_id": "c8a537a9-7f98-489b-8f8b-de2450c67ce6"}}, "fields": ["_id", "_index"]}'
UPDATE
Output of repr(result):
CompletedProcess(args=['curl', '-k', '-XGET', '-H', 'Content-Type: application/json', '-H', 'Authorization: ApiKey Q2txZ2FvSUJ6Nlcwa3pjbnh0NUM6enBidjdNLTdRQVNhc0tuTEk5UEZmZw==', 'https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200:9200/_search?index=.fleet-enrollment-api-keys-7&size=5&pretty', '-d', '{"query": {"match": {"_id": "c8a537a9-7f98-489b-8f8b-de2450c67ce6"}}, "fields": ["_id", "_index"]}'], returncode=3)
Since the error was
contains unrecognized parameter: [5]
The problem is not in the query but in the way you construct es_path. Instead of
&"+str(size)+
it should be
&size="+str(size)+
UPDATE:
Also from what I see your URL now looks like this
https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200:9200/_search?index=.fleet-enrollment-api-keys-7&size=5&pretty
and it's wrong for the following reasons:
the port is specified twice
the index is not at the right location
First, host should just be this (i.e. without port and path)
host="https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com"
Then you can build the URL like this instead:
es_path = host + ":" + str(port) + "/" + index + path +"?size="+str(size)+"&pretty"
i.e. it should look like this instead:
https://internal-a02c1b85dade940ef871b0b6d1e65191-1270244841.us-west-2.elb.amazonaws.com:9200/.fleet-enrollment-api-keys-7/_search?size=5&pretty
This question already has answers here:
FastApi 422 Unprocessable Entity, on authentication, how to fix?
(2 answers)
Closed last month.
I'm getting this error while trying to accept a pedantic model. After debugging for quite some time I believe the problem is with accepting CodeCreate
Pydantic model
class BaseCode(BaseModel):
index: Optional[int] = Field(None)
email: EmailStr
gen_time: datetime
expire_time: datetime
class CodeCreate(BaseCode):
code: int
used_time: Optional[datetime] = Field(None)
class Config:
orm_mode = True
class Message(BaseModel):
message: str
Code ORM
class Code(Base):
__tablename__ = 'code'
index = Column(Integer, primary_key=True, autoincrement=True)
code = Column(Integer)
email = Column(String, ForeignKey('user.email'))
gen_time = Column(DateTime)
expire_time = Column(DateTime)
used_time = Column(DateTime, nullable=True)
Handler
#app.post('/verify-code', response_model=schemas.Message, responses={404: {"model": schemas.Message}, 406: {"model": schemas.Message}})
async def verify_code(code: schemas.CodeCreate, response: Response, device_name: str = Body(..., embed=True), db=Depends(get_db)):
result = crud.verify_and_insert_code(db=db, code=code)
if result == 'matched':
response.status_code = status.HTTP_202_ACCEPTED
return crud.start_new_session(db=db, session=schemas.Session(session_id='1234', start_time=datetime.now(), email=code.email, device_name=device_name))
elif result == 'not-matched':
response.status_code = status.HTTP_200_OK
elif result == 'expire':
response.status_code = status.HTTP_406_NOT_ACCEPTABLE
elif result == 'invalid':
response.status_code = status.HTTP_404_NOT_FOUND
return schemas.Message(message="Item not found")
Body of request from the client
{
"code": {
"index": 0,
"email": "user#example.com",
"gen_time": "2022-01-24T16:38:12.612Z",
"expire_time": "2022-01-24T16:38:12.612Z",
"code": 0,
"used_time": "2022-01-24T16:38:12.612Z"
},
"device_name": "string"
}
Response body for 422
{
"detail": [
{
"loc": [
"body",
"code",
"email"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"code",
"gen_time"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"code",
"expire_time"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"code",
"code"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Temporary Solution
Removing this ORM confirmation code from Pydantic model solves the issue. I believe there might be a clash between CodeCreate pydantic model and Code ORM model, but I don't know how to resolve it.
class Config:
orm_mode = True
The 422 Unprocessable Entity error because of ContentType is incorrect. The FastAPI/Pydantic need ContentType = application/json to parse request body.
Are you sure your POST request has ContentType header is application/json?
If not add it!
According to MDN
here,
a 422 Unprocessable Entity means that the information of the request could not be processed.
In this case, the most likely problem is that the data of the POST request that is sent does not match with the Pydantic model.
Make sure the data that is sent is in the correct format.
Just as stated above by Brian Law, your request body is in the form of Code, which is not a pydantic model, but a database one.
When you send the POST request, the body should match up with CodeCreate, not Code.
Found the solution after debugging for quite a while.
The ORM configured pedantic model can't be used to receive requests from the client.
In my case, I had to create another class that extends the CodeCreate class add ORM configuration to that class and use CodeCreate for body from the client.
class BaseCode(BaseModel):
index: Optional[int] = Field(None)
email: EmailStr
gen_time: datetime
expire_time: datetime
class CodeCreate(BaseCode):
code: int
used_time: Optional[datetime] = Field(None)
class Code(CodeCreate):
class Config:
orm_mode = True
Post request
#app.post('/verify-code')
async def verify_code(code: schemas.CodeCreate):
return 'success'
Autentique API is Graphql. Documentation: https://docs.autentique.com.br/api/integracao/criando-um-documento
You must create an account on Autentique and an API key first.
Uploading the file in sandbox and sending it to an email for signing. It returns document's id and name.
Using curl
curl -H "Authorization: Bearer <TOKEN>" https://api.autentique.com.br/v2/graphql \
-F operations='{"query": "mutation CreateDocumentMutation($document: DocumentInput! $signers: [SignerInput!]! $file: Upload!) {createDocument(sandbox: true, document: $document, signers: $signers, file: $file) {id name }}", "variables": { "document": {"name": "<DOCUMENT_NAME>"}, "signers": [{"email": "<FROM_EMAIL>","action": "SIGN"}], "file": null } }' \
-F map='{ "0": ["variables.file"] }' \
-F 0=#<FULL_PATH_FILE>
Using aiogqlc
https://github.com/DoctorJohn/aiogqlc
import asyncio
from aiogqlc import GraphQLClient
endpoint = "https://api.autentique.com.br/v2/graphql"
headers = {
"Authorization": "Bearer <TOKEN>"
}
client = GraphQLClient(endpoint, headers=headers)
async def create_document():
query = """
mutation CreateDocumentMutation(
$document: DocumentInput!
$signers: [SignerInput!]!
$file: Upload!
) {
createDocument(
sandbox: true,
document: $document,
signers: $signers,
file: $file)
{
id
name
}
}"""
variables = {
"document": {
"name": "<DOCUMENT_NAME>"
},
"signers": [{
"email": "<FROM_EMAIL>",
"action": "SIGN"
}],
"file": open('<FULL_PATH_FILE>', 'rb'),
}
response = await client.execute(query, variables=variables)
print(await response.json())
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(create_document())
For more languages implementations: https://github.com/jaydenseric/graphql-multipart-request-spec#implementations
I am using DRF to update data. Its works good, but I am struggling how to update foreign keys.
{
"name": "Ready Or Not",
"releases": [
{
"platform": {
"name": "test"
},
"date": "2019-10-02T11:38:18Z"
}
]
},
This is a response of my API.
But I want to update this 'releases' information as well.
To sent I have this. If the 'platform' name doesnt exist it should also create one. How do I do this?
headers = {
'Authorization': 'Token ' + ss_token,
}
data = {
"releases": [
{
"platform": {
"name": "wanttoupdate"
},
"date": "2019-10-02T11:38:18Z"
},
]
}
source = Source.objects.all().first()
url = source.url + str(947) + '/'
response = requests.patch(url, headers=headers, data=data)
My models:
class Game(models.Model):
name = models.CharField(max_length=255)
class Platform(models.Model):
name = models.CharField(max_length=255)
class Release(models.Model):
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
game = models.ForeignKey(Game, related_name='releases', on_delete=models.CASCADE)
date = models.DateTimeField()
To post a ForeignKey instance , you have to use its id, for example in you case
if request.method == 'POST':
platform = request.POST['platform'] # pass your html name parameter
release_obj = Release(platform_id = platform)
release_obj.save()
Currently within the app, requesting a UberX instantly give you a exact quote price but in the Python API, I couldn't find it. I can only find the range of the cost. Where is the exact quote at?
Try to use "POST /v1.2/requests/estimate"
Example Request
curl -X POST \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Accept-Language: en_US' \
-H 'Content-Type: application/json' \
-d '{
"start_latitude": 37.7752278,
"start_longitude": -122.4197513,
"end_latitude": 37.7773228,
"end_longitude": -122.4272052
}' "https://api.uber.com/v1.2/requests/estimate"
I suggest you use "product_id" as well - to get the price for the product you need. Otherwise, if none is provided, it will default to the cheapest product for the given location.
You will get the response like:
{
"fare": {
"value": 5.73,
"fare_id": "d30e732b8bba22c9cdc10513ee86380087cb4a6f89e37ad21ba2a39f3a1ba960",
"expires_at": 1476953293,
"display": "$5.73",
"currency_code": "USD",
"breakdown": [
{
"type": "promotion",
"value": -2.00,
"name": "Promotion"
},
{
"type": "base_fare",
"notice": "Fares are slightly higher due to increased demand",
"value": 7.73,
"name": "Base Fare"
}
]
},
"trip": {
"distance_unit": "mile",
"duration_estimate": 540,
"distance_estimate": 2.39
},
"pickup_estimate": 2
}
Related to Pyton SDK - Please check: https://developer.uber.com/docs/riders/ride-requests/tutorials/api/python. You need to authenticate your user, and then get a product you want to use, and then get upfront fare (if product support this: upfront_fare_enabled field set to true). And after that you can book a ride. Code how to do it is in doc link I have sent as well:
# Get products for a location
response = client.get_products(37.77, -122.41)
products = response.json.get('products')
product_id = products[0].get('product_id')
# Get upfront fare and start/end locations
estimate = client.estimate_ride(
product_id=product_id,
start_latitude=37.77,
start_longitude=-122.41,
end_latitude=37.79,
end_longitude=-122.41,
seat_count=2
)
fare = estimate.json.get('fare')
# Request a ride with upfront fare and start/end locations
response = client.request_ride(
product_id=product_id,
start_latitude=37.77,
start_longitude=-122.41,
end_latitude=37.79,
end_longitude=-122.41,
seat_count=2,
fare_id=fare['fare_id']
)
request = response.json
request_id = request.get('request_id')
# Request ride details using `request_id`
response = client.get_ride_details(request_id)
ride = response.json
# Cancel a ride
response = client.cancel_ride(request_id)
ride = response.json