I would like to connect to spotify API and connect with OAuth and then search for a song a user requests.
I currently have a bot pulling for youtube and saving the file to play. I was wondering if there a way to do the same with Spotipy.
from flask import Flask, request, url_for, session, redirect
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import time
app = Flask(__name__)
app.secret_key = 'xxx'
app.config['SESSION_COOKIE_NAME'] = 'Token Cookie'
TOKEN_INFO = "token_info"
#app.route('/')
def login():
sp_oauth = create_spotify_oauth()
auth_url = sp_oauth.get_authorize_url()
return redirect(auth_url)
#app.route('/redirect')
def redirectPage():
sp_oauth = create_spotify_oauth()
session.clear()
code = request.args.get('code')
token_info = sp_oauth.get_access_token(code)
session[TOKEN_INFO] = token_info
return redirect(url_for('getTracks', _external=True))
#app.route('/getTracks')
def getTracks():
try:
token_info = get_token()
except:
print("user not logged in)")
redirect("/")
sp = spotipy.Spotify(auth = token_info["access_token"])
return
def get_token():
token_info = session.get(TOKEN_INFO, None)
if not token_info:
raise "execption"
now = int(time.time())
is_expired = token_info["expires_at"] - now < 60
if (is_expired):
sp_oauth = create_spotify_oauth()
token_info = sp_oauth.refresh_access_token(token_info['refresh_token'])
return token_info
def create_spotify_oauth():
return SpotifyOAuth(
client_id = 'CLIENT_ID',
client_secret = 'CLIENT_SECRET',
redirect_uri = url_for('redirectPage', _external = True),
scope= 'user-library-read')
I have a flask application that I am trying to test.
Right now I have a test that works correctly when I test for an http status code of 302, but when I add in follow_redirects = True I get no response from pytest when I run it and I'm not sure why.
Here's the class of tests that I am trying to run:
class TestEmailResetToken():
def test_email_reset(self, client, auth):
auth.login()
token = 'misshapentoken4856'
response = client.get(f'/confirm/{token}', follow_redirects = True)
assert response.status_code == 200
assert b'Update Your Signin Credentials' in response.data
def test_email_reset_valid_auth(self, app, client, auth):
token_data = {
'new_email': 'newemail#gmail.com',
'current_email': 'myemail#gmail.com'
}
with app.app_context():
token = generate_new_email_token(**token_data)
auth.login()
response = client.get(f'/confirm/{token}', follow_redirects = True)
assert response.status_code == 200
assert b'Email updated successfully' in response.data
with app.app_context():
email = confirm_token(token)
user = User.query.filter_by(email = email['new']).first()
assert user.email == email['new']
def test_email_reset_invalid_auth(self, app, client, auth):
token_data = {
'new_email': 'newemail#gmail.com',
'current_email': 'foo#bar.com'
}
with app.app_context():
token = generate_new_email_token(**token_data)
auth.login()
response = client.get(f'/confirm/{token}', follow_redirects = True)
assert response.status_code == 200
assert b'There was a problem processing your request' in response.data
My problem is that in test_email_reset_invalid_auth removing the follow_redirects option causes the test to complete, but when it's there the test just hangs.
What's more, if I comment out the test above it, test_email_reset_valid_auth, then it will work just fine. So I'm assuming it's in how I have my testing suite setup, rather than the actual unit test itself.
If it helps, here's the contents of my conftest.py file:
from app import create_app
from app.commands import init_db
from app.extensions import db as _db
from app.blueprints.user.models import User
from app.blueprints.user import extras
#pytest.fixture(scope='session')
def app():
"""Starts up a version of the application in testing mode to use for further tests"""
params = {
'DEBUG': False,
'TESTING': True,
'WTF_CSRF_ENABLED': False,
'SQLALCHEMY_DATABASE_URI': settings.SQLALCHEMY_TEST_DATABASE_URI
}
_app = create_app(settings_override=params)
with _app.app_context():
_db.drop_all()
_db.create_all()
# users with different settings for testing
test_users = [{
'email': 'myemail#gmail.com',
'password': 'Passw0rd!',
'sec_question': 'What is your favorite band?',
'sec_answer': 'built to spill'
},{
'email': 'myemail1#hotmail.com',
'password': 'Passw0rd!',
'sec_question': 'What is your favorite band?',
'sec_answer': 'Built to Spill',
'active': True
}]
for user_params in test_users:
user = User(**user_params)
_db.session.add(user)
_db.session.commit()
yield _app
#pytest.fixture(scope='function')
def client(app):
"""Yields a test version of the application"""
return app.test_client()
#pytest.fixture(scope='function')
def runner(app):
"""Yields a test version of the app to test command line functions"""
return app.test_cli_runner()
class Authentication():
"""Allows other test objects to login / logout"""
def __init__(self, client):
self._client = client
def login(self, email='myemail#gmail.com', password = 'Passw0rd!', follow_redirects=True):
return self._client.post('/login', data = {'email': email, 'password': password}, follow_redirects = follow_redirects)
def logout(self, follow_redirects = True):
return self._client.get('/logout', follow_redirects = follow_redirects)
#pytest.fixture(scope='function')
def auth(client):
return Authentication(client)
I just encountered this symptom. The cause of the failure was improper use of the url_for function as part of a redirect.
The offending code looked something like this:
redirect(url_for('route.index') + "/some/" + id + "/path")
and needed to be converted into something like this:
redirect(url_for('route.some_path_func', id=id))
I can't tell from the OP's code whether this is their exact problem, however almost certainly whatever is causing this symptom is a problem in the code being tested and not in the test code.
In my main.py, I have the following code-
app = FastAPI(docs_url="",)
app.add_middleware(SessionMiddleware, secret_key=os.getenv('SECRET'))
config = Config('.env')
oauth = OAuth(config)
CONF_URL = 'http://localhost:9090/.well-known/openid-configuration'
oauth.register(
name='google',
server_metadata_url=CONF_URL,
client_id=os.getenv('ID'),
client_secret=os.getenv('SECRET'),
client_kwargs={
'scope': 'openid email profile'
}
)
api_url = None
#app.get('/')
async def homepage(request: Request):
user = request.session.get('user')
if user:
data = json.dumps(user)
html = (
f'<pre>{data}</pre>'
'logout'
)
return HTMLResponse(html)
return HTMLResponse('login')
#app.get('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth')
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.get('/auth')
async def auth(request: Request):
try:
token = await oauth.google.authorize_access_token(request)
except OAuthError as error:
return HTMLResponse(f'<h1>{error.error}</h1>')
user = await oauth.google.parse_id_token(request, token)
request.session['user'] = dict(user)
if api_url:
return RedirectResponse(url=api_url)
return RedirectResponse(url='/')
#app.get('/logout')
async def logout(request: Request):
request.session.pop('user', None)
request.cookies.clear()
return RedirectResponse(url='/')
#app.get("/api")
async def get_swagger_ui(request: Request):
global api_url
api_url = request.url
user = request.session.get('user')
if user:
return get_swagger_ui_html(
openapi_url="/openapi.json", title="Webapp",)
else:
return RedirectResponse(
url="/login"
)
# routes
PROTECTED = [Depends(login)]
app.include_router(get_api.router, dependencies=PROTECTED)
In the get_api.py file, I have the following conf -
router = APIRouter()
#router.get("/api/higs", tags=["higs"])
def get_higs(db: Session = Depends(get_db),
)
try:
<something>
return x
except Exception as err:
raise HTTPException(
status_code=400, detail="Invalid parameter : {}".format(err),
)
There are similar other endpoints in the get_api.py file. I wanted to block access to these endpoints without authentication. So in the app.include_router method, I added dependencies. But its not working. I am able to access the endpoint data. For e.g. localhost:8000/api/higs - displays all the data in text that I would get from calling executing the GET endpoint in swagger UI. How can I fix this issue. Thanks.
I have the following app.py:
from flask import Flask
from waitress import serve
from bprint import api_blueprint
from errors import invalid_id, not_found, invalid_input, internal_server_error, unauthorized_access
app = Flask(__name__)
app.register_blueprint(api_blueprint)
app.register_error_handler(400, invalid_id)
app.register_error_handler(401, unauthorized_access)
app.register_error_handler(404, not_found)
app.register_error_handler(405, invalid_input)
app.register_error_handler(500, internal_server_error)
if __name__ == "__main__":
serve(app, host='localhost')
And the following code in bprint.py:
from flask import Blueprint, jsonify, request
import dbu
from models import Session, user_table, car_table, order_table
from schema import UserDetails, UserQuery, OrderDetails, OrderQuery, CarDetails, CarQuery, LoginData, \
ListUsersReq, Response
from contextlib import contextmanager
from flask_jwt_extended import jwt_required, create_access_token, get_jwt_identity
import datetime
api_blueprint = Blueprint('api', __name__)
#contextmanager
def session_scope():
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
else:
try:
session.commit()
except:
session.rollback()
raise
#api_blueprint.route("/login", methods=["POST"])
def login():
from app import bcrypt
data = LoginData().load(request.json)
if data:
user = dbu.get_entry_by_username(user_table, username=data["username"])
hpw = bcrypt.generate_password_hash(data["password"])
if not user:
return jsonify({"message": "Couldn't find user!"})
if bcrypt.check_password_hash(hpw, data["password"]):
access_token = create_access_token(identity=data["username"], expires_delta=datetime.timedelta(days=365))
return jsonify(access_token=access_token, id=user.id), 200
#api_blueprint.route("/user", methods=["GET"])
def list_users():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
args = ListUsersReq().load(request.args)
userlist = dbu.list_users(args.get("email"), args.get("username"))
return jsonify(UserDetails(many=True).dump(userlist))
else:
return jsonify(code=401, type='UNAUTHORIZED_ACCESS'), 401
#api_blueprint.route("/user", methods=["POST"])
def create_user():
with session_scope():
from app import bcrypt
user_details = UserQuery().load(request.get_json(force=True))
user_details["password"] = bcrypt.generate_password_hash(user_details["password"]).decode('UTF-8')
user = dbu.create_entry(user_table, **user_details)
access_token = create_access_token(identity=user.username, expires_delta=datetime.timedelta(days=365))
return jsonify(access_token=access_token, id=UserDetails().dump(user)["id"]), 200
#api_blueprint.route("/user/<int:id>", methods=["GET"])
def user_by_id(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
user = dbu.get_entry_by_id(user_table, id)
return jsonify(UserDetails().dump(user))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/user/<int:id>", methods=["PUT"])
def update_user(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin or user.id == id:
user_details = UserQuery().load(request.json)
user = dbu.get_entry_by_id(user_table, id)
dbu.update_entry(user, **user_details)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/user/<int:id>", methods=["DELETE"])
def delete_user(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin or user.id == id:
dbu.delete_entry(user_table, id)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars", methods=["GET"])
def get_inventory():
with session_scope():
cars = dbu.list_cars()
return jsonify(CarDetails(many=True).dump(cars))
#api_blueprint.route("/cars/car/<int:carId>", methods=["GET"])
def get_car_by_id(carId):
with session_scope():
car = dbu.get_car_by_id(car_table, carId)
return jsonify(CarDetails().dump(car))
#api_blueprint.route("/cars/car", methods=["POST"])
def create_car():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
car_details = CarQuery().load(request.json)
car = dbu.create_entry(car_table, **car_details)
return jsonify({"carId": CarDetails().dump(car)["carId"]})
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>", methods=["PUT"])
def update_car(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
car_details = CarQuery().load(request.json)
car = dbu.get_car_by_id(car_table, carId)
dbu.update_entry(car, **car_details)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>", methods=["DELETE"])
def delete_car(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
dbu.delete_car(car_table, carId)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order", methods=["POST"])
def place_order(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user:
order_data = OrderQuery().load(request.json)
order = dbu.create_entry(order_table,
userId=user.id,
carId=carId,
shipDate=order_data["shipDate"],
returnDate=order_data["returnDate"],
status="placed",
complete=False)
return jsonify({"id": OrderDetails().dump(order)["id"]})
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/orders", methods=["GET"])
def get_orders():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
orders = dbu.list_orders()
return jsonify(OrderDetails(many=True).dump(orders))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order/<int:orderId>", methods=["GET"])
def get_order_by_id(carId, orderId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user:
order = dbu.get_entry_by_id(order_table, id=orderId)
return jsonify(OrderDetails().dump(order))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order/<int:orderId>", methods=["DELETE"])
def delete_order(carId, orderId):
with session_scope():
dbu.delete_entry(order_table, id=orderId)
return jsonify(Response().dump({"code": "200"}))
When I try to run waitress-serve --port=5000 app:app I get the following error:
app.register_blueprint(api_blueprint)
raise AssertionError(
AssertionError: View function mapping is overwriting an existing endpoint function: api.wrapper
What may be the problem?
I'm almost sure it worked in December and now after reinstalling my Windows it doesn't
Now it also says I have too much code in my question and I don't know how to explain my problem with more words, so I have to add a few useless lines, sorry
Check if you have some custom decorators on views. Because flask take endpoint name either from #route parameter or from function name. In your case there're no endpoint parameter in any of functions.
Error says api.wrapper and it means that you have 2 or more function with name wrapper. Usually we see such name inside decorators. So you probably have decorator that looks like
def decorator(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
And some views are decorated with such decorator. And flask take "wrapper" as a view name
Try by commenting few lines of code - It may help you to resolve your issue.
from flask import Flask
from waitress import serve
from bprint import api_blueprint
# from errors import invalid_id, not_found, invalid_input, internal_server_error, unauthorized_access
app = Flask(__name__)
app.register_blueprint(api_blueprint)
# app.register_error_handler(400, invalid_id)
# app.register_error_handler(401, unauthorized_access)
# app.register_error_handler(404, not_found)
# app.register_error_handler(405, invalid_input)
# app.register_error_handler(500, internal_server_error)
if __name__ == "__main__":
serve(app, host='localhost')
I'm completely new to flask and web development in general. And what I need is to login to a website using steam id. I'm doing it as it said here, but get the following error:
OperationalError: (sqlite3.OperationalError) no such table: user
It seems to open up steam website correctly but it breaks when I press Log In. So, what's my mistake ? Any help is appreciated.
The code:
from flask import Flask, render_template, redirect, session, json, g
from flask_bootstrap import Bootstrap
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.openid import OpenID
import urllib
import re
app = Flask(__name__)
app.secret_key = '123'
Bootstrap(app)
app.config.from_pyfile('settings.cfg')
db = SQLAlchemy(app)
oid = OpenID(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
steam_id = db.Column(db.String(40))
nickname = db.String(80)
#staticmethod
def get_or_create(steam_id):
rv = User.query.filter_by(steam_id=steam_id).first()
if rv is None:
rv = User()
rv.steam_id = steam_id
db.session.add(rv)
return rv
def get_steam_userinfo(steam_id):
options = {
'key': app.config['STEAM_API_KEY'],
'steamids': steam_id
}
url = 'http://api.steampowered.com/ISteamUser/' \
'GetPlayerSummaries/v0001/?%s' % urllib.urlencode(options)
rv = json.load(urllib.urlopen(url))
return rv['response']['players']['player'][0] or {}
_steam_id_re = re.compile('steamcommunity.com/openid/id/(.*?)$')
#app.route('/login')
#oid.loginhandler
def login():
if g.user is not None:
return redirect(oid.get_next_url())
return oid.try_login('http://steamcommunity.com/openid')
#oid.after_login
def create_or_login(resp):
match = _steam_id_re.search(resp.identity_url)
g.user = User.get_or_create(match.group(1))
steamdata = get_steam_userinfo(g.user.steam_id)
g.user.nickname = steamdata['personaname']
db.session.commit()
session['user_id'] = g.user.id
flash('You are logged in as %s' % g.user.nickname)
return redirect(oid.get_next_url())
#app.before_request
def before_request():
g.user = None
if 'user_id' in session:
g.user = User.query.get(session['user_id'])
#app.route('/')
def homepage():
return render_template('mainpage.html')
#app.route('/logout')
def logout():
session.pop('user_id', None)
return redirect(oid.get_next_url())
if __name__ == '__main__':
app.run(debug=True)
You need to run a db.create_all() before running your app.
This will create all the tables described by your model in the database.
If you are new to flask you can follow the quickstart quide here