flask python creating swagger document error - python

I am trying to generate a swagger documentation for the API I am building in flask however when I execute below code I get very staring error, What I am missing here ?
This is what I have tried :
try:
from flask import Flask, g, request
from flask_restful import Resource, Api
from flask_limiter.util import get_remote_address
from flask_limiter import Limiter
from flasgger import Swagger
from flasgger.utils import swag_from
from flask_restful_swagger import swagger
from flask_httpauth import HTTPTokenAuth
import os
import json
except Exception as e:
print("some modules are missing {}".format(e))
app = Flask(__name__)
api = Api(app)
limiter = Limiter(app, key_func=get_remote_address)
limiter.init_app(app)
api = swagger.docs(Api(app), apiVersion='0.1', api_spec_url='/doc')
auth = HTTPTokenAuth(scheme='Token')
tokens = {
'awserftggyjkk34)ghtyrjrhhye34nnmw': 'cadmin',
'bwsosjhee(dhj345gtyuioplsertbsjkl': 'dadmin',
'mnchthas(sjklertyusvfgmbshdls234h': 'eadmin'
}
#auth.verify_token
def verify_token(token):
if token in tokens:
g.current_user = tokens[token]
return True
return False
class defr(Resource):
decorators = [limiter.limit("100/day")]
#swagger.model
#swagger.operation(notes='my notes ntes')
#auth.login_required
def currentuser(self):
return "Hello, %s!" % g.current_user
api.add_resource(defr, '/user')
error :
File "C:\Users\codamd\AppData\Local\Continuum\anaconda3\lib\site-packages\flask_restful_swagger\swagger.py", line 294, in extract_operations
for method in [m.lower() for m in resource.methods]:
TypeError: 'NoneType' object is not iterable
Any help would be great

The problem here is that you've used a custom named method in your defr class. The Flask-RESTful documentation specifies defining HTTP methods in your Resource class e.g.
class defr(Resource):
decorators = [limiter.limit("100/day")]
#swagger.model
#swagger.operation(notes='my notes ntes')
#auth.login_required
def get(self):
return "Hello, %s!" % g.current_user

Related

'message': 'The method is not allowed for the requested URL.'

I am getting this error when I run my python script to make a call to my API:
{'message': 'The method is not allowed for the requested URL.'}
I cannot figure out why I am getting this error as I am using the exact code as the tutorial I am following.
Here is the call to my API:
import requests
BASE = "http://127.0.0.1:5000/"
response = requests.put(BASE + "video/1", {"likes": 10})
print(response.json())
And here is my API:
from flask import Flask
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
video_put_args = reqparse.RequestParser()
video_put_args.add_argument("name",type=str, help="t")
video_put_args.add_argument("views",type=int, help="t")
video_put_args.add_argument("likes",type=int, help="t")
videos = {}
class Video(Resource):
def get(self, video_id):
return videos[video_id]
def put(self, video_id):
args=video.args.parse_args()
return {videoid:args}
api.add_resource(Video, "/video/<int:video_id>")
if __name__ == "__main__":
app.run(debug=True)
Any help would be appreciated, thanks

Flask restplus exception is not caught by api.errorhandler

I want to define custom error handling for a Flask-restful API
The flask custom error(FlaskGenericException) is raised but is not handled by restplus errorhandler. it just throws a 500 Internal Server error.
Not sure why the dict defined inside exceptions.py is not thrown as api response.
Expected Result
{'user': 'Lionel Messi', 'company': 'Soccer Brazil','message': 'A generic error'}
Here are my files that I am using to test this:
app.py
import json,os
import logging
import sys
import requests
import config
from os import environ
from flask import Flask, request, jsonify, Response
from flask_jwt_extended import (JWTManager, jwt_required)
from flask_restplus import Api, Resource, reqparse, fields, inputs
from cal_Test import calTest
from exceptions import FlaskGenericException
# Setup Flask Server
app = Flask(__name__,root_path=os.path.join(os.getcwd(), './'))
app.config.from_object(config.Config)
api = Api(app=app,
version='0.1',
title="Test Cal API",
doc="/swagger/",
description='TestCal = Cal Test')
ns = api.namespace('/', description='Cal Test Api')
#api.errorhandler(FlaskGenericException)
def handle_error(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
resource_parser = reqparse.RequestParser()
resource_parser.add_argument('test', type=str, required=True)
resource_flds = api.model('Resource', {
'test': fields.String(required = True),
})
#ns.route('/test', methods=['POST'])
class TestCal(Resource):
# #jwt_required
#ns.doc(body=resource_flds)
#ns.expect(resource_flds, validate=True)
def post(self):
request_data = resource_parser.parse_args()
caltst = calTest()
result = caltst.cal_test(request_data)
response = app.response_class(
response=json.dumps(result),
status=200,
mimetype='application/json'
)
return response
if __name__ == "__main__":
app.run(debug=True)
cal_Test.py
import pandas as pd
import pyodbc
import scipy.stats as st
import numpy as np
import pandas as pd
from scipy.stats import norm
from flask import Flask, request, jsonify
import os
from exceptions import FlaskGenericException
class calTest:
def __init__(self):
self.test_name = 'Hi! I am test'
def cal_test(self,reqdata):
if self.test_name == reqdata['test']:
return "Successfull"
else:
raise FlaskGenericException('A generic error', status_code=404, payload={'user': 'Lionel Messi', 'company': 'Soccer Brazil'})
exceptions.py
class FlaskGenericException(Exception):
status_code = 500 # default unless overridden
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
Ran into the same issue, I ended up using the abort method from Flask. I don't know if it fits your use case, but give it a try.
from flask import abort
def cal_test(self,reqdata):
if self.test_name == reqdata['test']:
return "Successfull"
else:
abort(404, "A generic error")

How to mock response from integrated service while testing server

The long story short is I am working on building a server that serves as something as a chat bot. The server uses google dialog flow. Right now I have an endpoint exposed that allows me to talk to my server, when I hit that endpoint, google auth, as well as google dialog flow gets called. I am attempting to mock the response of dialog flow while leaving the actual server to respond to the network call. As of now my test looks like this.
This is my base test file:
import unittest
import mock
class BaseTest(unittest.TestCase, object):
def __init__(self, *args, **kwargs):
super(BaseTest, self).__init__(*args, *kwargs)
def auto_patch(self, patch_target):
patcher = mock.patch(patch_target)
patched = patcher.start()
self.addCleanup(patcher.stop)
return patched
This is my test file:
import json
import uuid
from os import path
from tests.base_test import BaseTest
from agent.api_service import app
import requests_mock
import pytest
from hamcrest import assert_that, has_items, equal_to
CWD = path.dirname(path.realpath(__file__))
class TestAudio(BaseTest):
def test__interact__full_no_stt(self):
payload = json.load(open("tests/json_payloads/test__interact__full_audio.json"))
u_session_id = str(uuid.uuid1())
payload["session_id"] = u_session_id
#mock a 500 back from STT
with open("tests/json_payloads/stt_500.json", "r") as issues_file:
mock_response = issues_file.read()
with requests_mock.Mocker() as m:
m.register_uri('POST', 'https://speech.googleapis.com/v1/speech:recognize', text=mock_response)
request, response = app.test_client.post("/agent_service/interact", data=json.dumps(payload))
self.assertEqual(200, response.status)
This is my google stt file:
import json
import requests
from agent.exceptions import GoogleSTTException
from agent.integrations.google.google_auth_service import get_auth_token
from agent.integrations.google.google_stt_request import GoogleSTTRequest
from agent.integrations.google.google_stt_response import GoogleSTTResponse
def speech_to_text(audio_string):
try:
google_stt_request = GoogleSTTRequest(audio_string).to_payload()
request_headers = dict()
request_headers['Authorization'] = 'Bearer ' + get_auth_token()
request_headers['Content-Type'] = 'application/json'
url = 'https://speech.googleapis.com/v1/speech:recognize'
google_response = requests.post(url, data=json.dumps(google_stt_request), headers=request_headers)
response = GoogleSTTResponse(google_response.json())
return response
except Exception as e:
raise GoogleSTTException('Received an error invoking google stt {}'.format(e))
Does anyone have any ideas on how I can mock the response from the google stt call, without touching the google auth call or the server call itself? I have tried a handful of things and so far no luck. I either end up mocking nothing, or both the google stt and auth call.
So I ended up moving away from the original implementation, but this is what got me there.
#responses.activate
def test__interact__full_no_stt(self):
payload = json.load(open("tests/json_payloads/test__interact__full_audio.json"))
u_session_id = str(uuid.uuid1())
payload["session_id"] = u_session_id
#mock a 500 back from STT
responses.add(responses.POST,
'https://speech.googleapis.com/v1/speech:recognize',
json={'error': 'broken'}, status=500)
request, response = app.test_client.post("/agent_service/interact", data=json.dumps(payload))
self.assertEqual(200, response.status)
result = response.json
Responses makes this much easier, just be sure to include the annotation at the top of the test.

Google API Client, calling the built API with dot in parameter name

Im on Google App Engine, I try to call the a specific method of the Monitoring API via Google API Client. When I call the timeSeries.list with interval.startTime then the error is SyntaxError: keyword can't be an expression. When I replace interval.startTime and interval.EndTime with interval=intervalobj the error is:
File "/base/data/home/apps/e~bwm2-bgi/scaler:
scaling-readmon.412218217025616715/lib/googleapiclient/discovery.py",
line 716, in method raise TypeError
('Got an unexpected keyword argument "%s"' % name)
TypeError: Got an unexpected keyword argument "interval"
I used the Compute API in the same manner like interval=intervalobland it worked. Any tip is appreciated.
CODE:
import webapp2
import logging
from google.appengine.ext import vendor
vendor.add('lib')
from google.appengine.api import app_identity
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
monitoring = discovery.build('monitoring','v3', credentials=GoogleCredentials.get_application_default())
class Scaler(webapp2.RequestHandler):
def post(self):
'''
req = monitoring.projects().metricDescriptors().list(name='projects/PROJ')
res = req.execute()
logging.info(res)
'''
intervalobj = {
'startTime': '2018-08-10T11:01:23.045123456Z',
'endTime': '2018-08-10T11:01:23.045123456Z'
}
res = monitoring.projects().timeSeries().list(
name = 'projects/bwm2-bgi',
filter = 'metric.type="appengine.googleapis.com/http/server/response_style_count"',
interval.startTime = '2018-08-10T11:01:23.045123456Z',
interval.endTime = '2018-08-28T11:01:23.045123456Z').execute()
logging.info(res)
app = webapp2.WSGIApplication([
('/scaler', Scaler)
], debug=True)
Using interval_startTime and interval_endTime worked in place of the interval.startTime and interval.endTime for me.
request = monitor.projects().timeSeries().list(name=project_name,
interval_startTime='2019-03-19T06:00:00.045123456Z',
interval_endTime='2019-03-19T07:00:00.045123456Z',
filter='metric.type="appengine.googleapis.com/http/server/response_style_count"')
I believe setting a point interval with interval.endTime is used when specifying points with the monitoring_v3.MetricServiceClient TimeSeries but not with the discovery resource.

what does url_for('.outbound', _external=True) means?

I have a python Twilio code like this(Click to Call method in twilio):
from flask import Flask
from flask import jsonify
#from flask import render_template
#from flask import request
from flask import url_for
from twilio.twiml.voice_response import VoiceResponse
from twilio.rest import Client
app = Flask(__name__)
# Voice Request URL
#app.route('/call')
def call():
# Get phone number we need to call
phone_number = request.form.get('phoneNumber', None)
try:
twilio_client = Client(app.config['TWILIO_ACCOUNT_SID'],
app.config['TWILIO_AUTH_TOKEN'])
except Exception as e:
msg = 'Missing configuration variable: {0}'.format(e)
return jsonify({'error': msg})
try:
twilio_client.calls.create(from_=app.config['TWILIO_CALLER_ID'],
to=phone_number,
url=url_for('.outbound', _external=True))
except Exception as e:
app.logger.error(e)
return jsonify({'error': str(e)})
return jsonify({'message': 'Call incoming!'})
#app.route('/outbound', methods=['POST'])
def outbound():
response = VoiceResponse()
response.say("Thank you for contacting our sales department. If this "
"click to call application was in production, we would "
"dial out to your sales team with the Dial verb.",
voice='alice')
response.number("+16518675309")
return str(response)
if __name__ == '__main__':
app.run()
When i try run run this from browser by calling : http://localhost:5000/call
i am getting ERROR: Unable to create record: Url is not a valid url:
How to call the Outbound function in the url and start the conversation between two people.
Instead of url_for('.outbound', _external=True) you should use url_for('outbound'). The docs linked by stamaimer say:
In case blueprints are active you can shortcut references to the same blueprint by prefixing the local endpoint with a dot (.).
You do not need a dot at the beginning. Check how url building is handled in flask.

Categories