I'm working on fastapi web app and currently strugling with one thing.
I'm working on google auth system and my dependency injection does not work as expected.
Here are my endpoints:
#app.route('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth')
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.route('/auth')
async def auth(request: Request):
try:
access_token = await oauth.google.authorize_access_token(request)
except OAuthError as e:
raise CREDENTIALS_EXCEPTION
_data = access_token["userinfo"]
if get_or_create_user(data = _data):
return JSONResponse({'result': True, 'access_token': create_token(_data['email'])})
raise CREDENTIALS_EXCEPTION
The problem lies in get_or_create_user function.
#validate_payload
def get_or_create_user(data: USER_GOOGLE_DATA, users_repo: UsersRepository = Depends(SQLUsersRepository)) -> model.User:
_email: str = data.get("email")
if not _email:
raise AttributeError("Payload is missing data")
user: model.User = users_repo.get_by_email(email=_email)
if not user:
_user = model.User(**dict(data))
user = users_repo.add(model.User(_user))
return user
data is dict with required info to create user and users_repo is crud class. Decorator is just to check if data contains all required keys.
PAYLOAD_KEYS: Tuple[str] = ("email",)
def validate_payload(func: Callable[[USER_GOOGLE_DATA, UsersRepository], model.User]):
def decorator(*args, **kwargs):
_data: USER_GOOGLE_DATA = kwargs.get("data")
for _key in PAYLOAD_KEYS:
if _key not in _data:
return None
return func(_data)
return decorator
My crud class looks like following:
class SQLUsersRepository(input_boundries.UsersRepository):
def __init__(self, db: Session or RawDBConnection = Depends(get_db)):
self._db = db
def get(self, user_id: UserId) -> model.User:
user_db = self._db.query(model.User).filter(model.User.id == user_id).first()
if not user_db:
raise DBRecordNotFound(f"User with id: '{user_id}' not found.")
return user_db
def get_by_email(self, email: str) -> model.User:
user_db = self._db.query(model.User).filter(model.User.email == email).first()
if not user_db:
raise DBRecordNotFound(f"User with email: '{email}' not found.")
return user_db
def add(self, user: model.User) -> model.User:
self._db.add(user)
self._db.commit()
self._db.refresh(user)
return user
def save(self, user: model.User) -> None:
user_db = self._db.query(model.User).filter(model.User.id == user.id).first()
self.add(user=user_db)
def remove(self, user_id: UserId) -> None:
user_db = self._db.query(model.User).filter(model.User.id == user_id).first()
if not user_db:
raise DBRecordNotFound(f"User with id: '{user_id}' not found.")
user_db.delete()
self._db.commit()
The error I receive AttributeError: 'Depends' object has no attribute 'get_by_email'
What's wrong with these dependiences? As far as I remember Depends() works similar to decorator (it triggers first).
Related
I've run into a bit of a snag on this code and could use some help, I can't manage to figure out what is the issue here.
File "C:\Users\Joshua\Desktop\sasquatch_sightings\flask_app\controllers\users.py", line 34, in user_dashboard
return render_template("dashboard.html",user=User.get_user_by_id(data),sightings=Sighting.get_all())
File "C:\Users\Joshua\Desktop\sasquatch_sightings\flask_app\models\user.py", line 44, in get_user_by_id
return cls(results[0])
IndexError: tuple index out of range
My Code
Users.py
from flask_app import app
from flask import render_template, redirect, request, session, flash
from flask_app.models.user import User
from flask_app.models.sighting import Sighting
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/user/create', methods={'POST'})
def create_user():
if not User.validate_user_reg_data(request.form):
return redirect('/')
data = {
"first_name": request.form['first_name'],
"last_name": request.form['last_name'],
"email": request.form['email'],
"password": bcrypt.generate_password_hash(request.form['password'])
}
id = User.create_user(data)
session['user_id'] = id
return redirect('/user/dashboard')
#app.route('/user/dashboard')
def user_dashboard():
if 'user_id' not in session:
return redirect('/logout')
data = {
'id': session ['user_id']
}
return render_template("dashboard.html",user=User.get_user_by_id(data),sightings=Sighting.get_all())
#app.route('/user/login', methods = ['POST'])
def login():
user = User.get_user_by_email(request.form)
if not user:
flash("Invalid Email, login")
return redirect('/')
session['user_id'] = user.id
return redirect('/user/dashboard')
#app.route('/user/logout')
def logout():
session.clear()
return redirect('/')
2nd Code User.py
from flask_app.config.mysqlconnection import MySQLConnection, connectToMySQL
from flask import flash, session
import re
EMAIL_REGEX = re.compile(r'^[a-zA-Z0-9.+_-]+#[a-zA-Z0-9._-]+\.[a-zA-Z]+$')
class User:
db = "sasquatch"
def __init__(self,data):
self.id = data['id']
self.first_name = data['first_name']
self.last_name = data['last_name']
self.email = data['email']
self.password = data['password']
self.created_at = data['created_at']
self.updated_at = data['updated_at']
#classmethod
def create_user(cls, data):
data = cls.parse_registration_data(data)
query = "INSERT INTO users (first_name, last_name, email, password) VALUES (%(first_name)s, %(last_name)s, %(email)s, %(password)s);"
return connectToMySQL(cls.db).query_db(query, data)
#classmethod
def get_all(cls):
query = "SELECT * FROM users;"
results = connectToMySQL(cls.db).query_db(query)
users = []
for row in results:
users.append( cls(row))
return users
#classmethod
def get_user_by_email(cls, data):
query = "SELECT * FROM users WHERE email = %(email)s;"
results = connectToMySQL(cls.db).query_db(query,data)
if len(results) < 1:
return False
return cls(results[0])
#classmethod
def get_user_by_id(cls,data):
query = "SELECT * FROM users WHERE id = %(id)s;"
results = connectToMySQL(cls.db).query_db(query,data)
return cls(results[0])
#staticmethod
def validate_user_reg_data(user):
is_valid = True
query = "SELECT * FROM users WHERE email = %(email)s;"
results = connectToMySQL(User.db).query_db(query,user)
if len(results) >= 1:
flash("Email already taken.","register")
is_valid=False
if not EMAIL_REGEX.match(user['email']):
flash("Invalid Email","register")
is_valid=False
if len(user['first_name']) < 2:
flash("First name must be more than 2 characters","register")
is_valid= False
if len(user['last_name']) < 2:
flash("Last name must be more than 2 characters", "register")
if len(user['password']) < 8:
flash("Password must be more than 8 characters","register")
is_valid= False
if user['password'] != user['confirm']:
flash("Passwords do not match","register")
return is_valid
#staticmethod
def parse_registration_data(data):
parsed_data = {}
parsed_data['email'] = data['email'].lower()
parsed_data['first_name'] = data['first_name']
parsed_data['last_name'] = data['last_name']
return parsed_data
I have created a lambda
where I retrieve region, based on the context.invoked_function_arn.
So, I would like to create and emulate this behavior using a test script file not via conftest.py because I have other unit test methods implemented in the same test script.
I find this link lambda-context which is useful and implemented the same concept in my test script. However all the time, I'm getting the AttributeError: 'function' object has no attribute 'invoked_function_arn' error message.
#pytest.fixture
def mock_lambda_context():
class ClientContext:
"""
Class for mocking Context
Has `custom`, `env`, and `client` `__slots__`
"""
__slots__ = ["custom", "env", "client"]
def make_obj_from_dict(_class, _dict, fields=None):
"""
Makes an object of `_class` from a `dict`
:param _class: A class representing the context
:type _class: `ContextClass`
:param _dict: A dictionary of data
:type _dict: `dict`
:param fields: [description], defaults to None
:type fields: [type], optional
:return: An object
:rtype: `ClientContext` class
"""
if _dict is None:
return None
obj = _class()
set_obj_from_dict(obj, _dict)
return obj
def set_obj_from_dict(obj, _dict, fields=None):
if fields is None:
fields = obj.__class__.__slots__
for field in fields:
setattr(obj, field, _dict.get(field, None))
class LambdaContext(object):
"""
Create a Lambda Context Class object
"""
def __init__(self, invokeid, client_context, invoked_function_arn=None):
self.aws_request_id = invokeid
self.log_group_name = "AWS_LAMBDA_LOG_GROUP_NAME"
self.log_stream_name = "AWS_LAMBDA_LOG_STREAM_NAME"
self.function_name = "AWS_LAMBDA_FUNCTION_NAME"
self.memory_limit_in_mb = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE"
self.function_version = "AWS_LAMBDA_FUNCTION_VERSION"
self.invoked_function_arn = "arn:aws:lambda:eu-west-1:123456789012:function:" \
"ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
self.client_context = make_obj_from_dict(ClientContext, client_context)
if self.client_context is not None:
self.client_context.client = None
self.identity = None
def get_remaining_time_in_millis(self):
return None
def log(self, msg):
str_msg = str(msg)
print(str_msg)
context = LambdaContext("AWS_ID", {})
return context
def test_handler(events, mock_lambda_context):
res = handler(events, mock_lambda_context)
assert res['context.invoked_function_arn'] == "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
Can anyone point me how to fix this silly one?
I created a sample_context.json like this:
{
"invoked_function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"log_group_name": "/aws/lambda/ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"function_name": "ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"function_version": "$LATEST"
}
Then in my main test file:
#pytest.fixture
def context_outcome():
return load_json_from_file('sample_context.json')
#pytest.fixture
def context(context_outcome):
response = type('new', (object,), context_outcome)
seqs = tuple, list, set, frozenset
for i, j in context_outcome.items():
if isinstance(j, dict):
setattr(response, i, context(j))
elif isinstance(j, seqs):
setattr(response, i,
type(j)(context(sj) if isinstance(sj, dict) else sj for sj in j))
else:
setattr(response, i, j)
return response
def test_main(events, context):
assert context.invoked_function_arn == "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
I got around this by doing the following:
from dataclasses import dataclass
#pytest.fixture
def context():
#dataclass
class LambdaContext:
function_name: str = "test"
aws_request_id: str = "88888888-4444-4444-4444-121212121212"
invoked_function_arn: str = "arn:aws:lambda:eu-west-1:123456789101:function:test"
return LambdaContext()
def test_lambda(context):
my_class = MyClass(EVENT, context)
from Exception.Exceptions import *
import re
class Repo(object):
def __init__(self):
self.__items_dictionary = {}
def get_element(self, id):
try:
return self.__items_dictionary[id]
except KeyError:
raise RepositoryError("ID not existent")
def add_item(self, new_item):
try:
if new_item.id in self.__items_dictionary.keys():
raise RepositoryError("Id already exists!")
self.__items_dictionary[new_item.id] = new_item
except AttributeError:
raise RepositoryError('Element does not have an id introduced!')
def remove_item(self, id):
try:
if id in self.__items_dictionary.keys():
return self.__items_dictionary.pop(id)
else:
raise RepositoryError("Element does not exist!")
except KeyError:
raise RepositoryError("Element with that id does not exist!")
def update_item(self, id, attribute, new_value):
if id in self.__items_dictionary.keys():
if hasattr(self.__items_dictionary[id], attribute):
setattr(self.__items_dictionary[id], attribute, new_value)
else:
raise RepositoryError("Element with that attribute does not exist!")
else:
raise RepositoryError('Element with that id does not exist!')
def search_item(self, attribute, search_value):
searched_items = []
try:
if attribute == 'id':
search_value = int(search_value)
if search_value in self.__items_dictionary.keys():
searched_items.append(self.__items_dictionary[search_value])
else:
search_value = search_value.lower()
for item in self.__items_dictionary.keys():
item_string = getattr(self.__items_dictionary[item], attribute).lower()
if re.findall(search_value, item_string):
searched_items.append(self.__items_dictionary[item])
return searched_items
except ValueError as ve:
raise RepositoryError(ve)
#property
def get_all(self):
return self.__items_dictionary
def get_all_for_file(self):
return self.__items_dictionary
class FileRepository(Repo):
def __init__(self,filename, read_entity, write_entity):
Repo.__init__(self)
self.__filename = filename
self.__read_entity = read_entity
self.__write_entity = write_entity
def _read_all_from_file(self):
self._entities = {}
with open(self.__filename,'r') as file:
lines = file.readlines()
for line in lines:
line = line.strip()
if line != "":
entity = self.__read_entity(line)
self._entities[entity.id] = entity
def _write_all_to_file(self):
with open(self.__filename, 'w') as file:
for entity in self._entities:
line = self.__write_entity(entity)
file.write(line + "\n")
def add_item(self,new_item):
self._read_all_from_file()
Repo.add_item(self, new_item)
self._write_all_to_file()
def search_item(self, attribute, search_value):
self._read_all_from_file()
return Repo.search_item(self, attribute,search_value)
def update_item(self, id, attribute, new_value):
self._read_all_from_file()
Repo.update_item(self, id,attribute,new_value)
self._write_all_to_file()
def remove_item(self, id):
self._read_all_from_file()
Repo.remove_item(self, id)
self._write_all_to_file()
def get_all(self):
self._read_all_from_file()
return Repo.get_all
def get_element(self, id):
self._read_all_from_file()
return Repo.get_element(self,id)
I have checked and all gets added to it but whenever I try to access anything I get something like 'function' object has no attribute 'values' or 'method' object is not iterable. Could it be that I am calling that #property? But even when I tried with get_all_for_file() it still did't work...
I have two identical methods that update a list by some value:
def block_device(self, device_id):
if self.block_device_ids is None:
self.block_device_ids = []
if device_id not in self.block_device_ids:
self.block_device_ids.append(device_id)
self.save()
return True
return False
def add_video(self, video_id):
if self.video_ids is None:
self.video_ids = []
if video_id not in self.video_ids:
self.video_ids.append(video_id)
self.save()
return True
return False
How to create one method update_collectionand use it in both cases?
I created the following solution:
async def update_collection(self, collection, item, attr_name):
if collection is None:
collection = []
if item not in collection:
getattr(self, attr_name).append(item)
await self.save()
return True
return False
async def add_video(self, video_id):
return await self.update_collection(self.video_ids, video_id, 'video_ids')
async def block_device(self, device_id):
return await self.update_collection(self.block_device_ids, device_id, 'device_ids')
but it doesn't work because of collection = []. How to fix this?
Is there anything that I can improve?
You don't need to pass in the collection and the name of the attribute:
async def update_collection(self, item, attr_name):
collection = getattr(self, attr_name)
if collection is None:
setattr(self, attr_name, [])
collection = getattr(self, attr_name)
if item not in collection:
collection.append(item)
await self.save()
return True
return False
NOTE: You have a bug on the last line of your code: the attr_name passed in should be "block_device_ids" not "device_ids"
class account(object):
__duser_id = ''
__duser_name =''
__duser_no = ''
def __init__(self, default, entry_name, password, user_id='', user_name='', user_no=''):
if type(default) != bool:
raise Exception("Error 0x1: type(default) is boolean ")
if default == False:
self.__user_id = user_id
self.__user_name = user_name
self.__user_no = user_no
else:
self.__user_id = __duser_id
self.__user_name = __duser_name
self.__user_no = __duser_no
self.__entry_name = entry_name
self.__password = password
def dset(self, duser_id=__duser_id, duser_name=__duser_name, duser_no=__duser_no):
__duser_id = duser_id
__duser_name = duser_name
__duser_no = duser_no
return (__duser_id, __duser_name, __duser_no)
def dget(self):
return (__duser_id, __duser_name, __duser_no)
def set(self, user_name=self.__user_name, user_id=self.__user_id, user_no=self.__user_no, password=self.__password):
self.__user_id = user_id
self.__user_name = user_name
self.__user_no = user_no
self.__password = password
return (self.__user_id, self.__user_name, self.__user_no, self.password)
def get(self):
return (self.__user_id, self.__user_name, self.__user_no, self.password)
if __name__ == '__main__':
gmail = account(default=True, entry_name='gmail', password='pass***')
print(gmail.dget())
print(gmail.get())
out put is:
Traceback (most recent call last):
File "interface.py", line 1, in
class account(object):
File "interface.py", line 30, in account
def set(self, user_name=self.__user_name, user_id=self.__user_id, user_no=self.__user_no, password=self.__password):
NameError: name 'self' is not defined
Ok o got it.
but there is another one i just changed code.
This is a decorator with arbitrary number of arguments and keyword
arguments
def user_no_is_number(func):
def wrapper(*args, **kargs):
if 'user_no' in kargs:
if type(kargs['user_no']) != int:
raise Exception('Error 1x0: user_no must contains only numbers.')
else:
return func(*args, **kargs)
return wrapper
#staticmethod
#user_no_is_number
def dset(user_id=None, user_name=None, user_no=None):
if user_id:
account.__duser_id = user_id
if user_name:
account.__duser_name = user_name
if user_no:
account.__duser_no = user_no
return (account.__duser_id, account.__duser_name, account.__duser_no)
but the dset() function return always None
*I think there is problem with arbitrary keywords parameters. by using **kargs in decorator parameter it becomes dictionary and by again passing **kargs it just return values of that dictionary.*