Finished register.

This commit is contained in:
retoor 2025-01-24 21:19:03 +01:00
parent be9489f939
commit 2ba55f692d
12 changed files with 129 additions and 70 deletions

View File

@ -4,6 +4,8 @@ from aiohttp import web
from app.app import Application as BaseApplication
from app.cache import time_cache_async
from jinja_markdown2 import MarkdownExtension
from snek.mapper import get_mappers
from snek.service import get_services
from snek.system import http
from snek.system.middleware import cors_middleware
from snek.view.about import AboutHTMLView, AboutMDView
@ -14,6 +16,7 @@ from snek.view.login_form import LoginFormView
from snek.view.register import RegisterView
from snek.view.register_form import RegisterFormView
from snek.view.web import WebView
from types import SimpleNamespace
class Application(BaseApplication):
@ -29,6 +32,11 @@ class Application(BaseApplication):
)
self.jinja2_env.add_extension(MarkdownExtension)
self.setup_router()
self.setup_services()
def setup_services(self):
self.services = SimpleNamespace(**get_services(app=self))
self.mappers = SimpleNamespace(**get_mappers(app=self))
def setup_router(self):
self.router.add_get("/", IndexView)

View File

@ -22,4 +22,3 @@ class LoginForm(Form):
type="button"
)

View File

@ -1,10 +1,19 @@
from snek.system.form import Form, HTMLElement,FormInputElement,FormButtonElement
class UsernameField(FormInputElement):
@property
async def errors(self):
result = await super().errors
if self.value and await self.app.services.user.count(username=self.value):
result.append("Username is not available.")
return result
class RegisterForm(Form):
title = HTMLElement(tag="h1", text="Register")
username = FormInputElement(
username = UsernameField(
name="username",
required=True,
min_length=2,

View File

@ -3,4 +3,4 @@ from snek.model.user import UserModel
class UserMapper(BaseMapper):
table_name = "user"
model: UserModel
model_class = UserModel

View File

@ -1,13 +1,14 @@
from snek.system.service import BaseService
from snek.system import security
class UserService:
class UserService(BaseService):
mapper_name = "user"
async def create_user(self, username, password):
async def register(self, email, username, password):
if await self.exists(username=username):
raise Exception("User already exists.")
model = await self.new()
model.email = email
model.username = username
model.password = await security.hash(password)
if await self.save(model):

View File

@ -280,7 +280,11 @@ class GenericForm extends HTMLElement {
if(e.detail.type == "button"){
if(e.detail.value == "submit")
{
await me.validate()
const isValid = await me.validate()
if(isValid){
const isProcessed = await me.submit()
console.info({processed:isProcessed})
}
}
}
@ -294,13 +298,15 @@ class GenericForm extends HTMLElement {
async validate(){
const url = this.getAttribute("url")
const me = this
const response = await fetch(url,{
let response = await fetch(url,{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({"action":"validate", "form":me.form})
});
const form = await response.json()
Object.values(form.fields).forEach(field=>{
if(!me.form.fields[field.name])
@ -320,6 +326,22 @@ class GenericForm extends HTMLElement {
console.info(field.errors)
me.fields[field.name].setErrors(field.errors)
})
console.info({XX:form})
return form['is_valid']
}
async submit(){
const me = this
const url = me.getAttribute("url")
const response = await fetch(url,{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({"action":"submit", "form":me.form})
});
return await response.json()
}
}
customElements.define('generic-form', GenericForm);

View File

@ -35,8 +35,8 @@ class HTMLElement(model.ModelField):
self.html = html
super().__init__(name=name, *args, **kwargs)
def to_json(self):
result = super().to_json()
async def to_json(self):
result = await super().to_json()
result['text'] = self.text
result['id'] = self.id
result['html'] = self.html
@ -53,8 +53,8 @@ class FormInputElement(FormElement):
self.place_holder = place_holder
self.type = type
def to_json(self):
data = super().to_json()
async def to_json(self):
data = await super().to_json()
data["place_holder"] = self.place_holder
data["type"] = self.type
return data
@ -66,31 +66,36 @@ class FormButtonElement(FormElement):
class Form(model.BaseModel):
@property
def html_elements(self):
json_elements = super().to_json()
return [element for element in self.fields if isinstance(element, HTMLElement)]
def set_user_data(self, data):
return super().set_user_data(data.get('fields'))
def to_json(self, encode=False):
elements = super().to_json()
async def to_json(self, encode=False):
elements = await super().to_json()
html_elements = {}
for element in elements.keys():
if element == 'is_valid':
# is_valid is async get property so we can't do getattr on it
continue
field = getattr(self, element)
if isinstance(field, HTMLElement):
try:
html_elements[element] = elements[element]
except KeyError:
pass
return dict(fields=html_elements, is_valid=self.is_valid, errors=self.errors)
is_valid = all(field['is_valid'] for field in html_elements.values())
return dict(fields=html_elements, is_valid=is_valid, errors=await self.errors)
@property
def errors(self):
async def errors(self):
result = []
for field in self.html_elements:
result += field.errors
result += await field.errors
return result
@property
def is_valid(self):
return all(element.is_valid for element in self.html_elements)
async def is_valid(self):
# This is not good, but timebox to resolve issue exceeded.
return False

View File

@ -1,25 +1,19 @@
DEFAULT_LIMIT = 30
import typing
from snek.system.model import BaseModel
from snek.app import Application
import types
class Mapper:
class BaseMapper:
model_class:BaseModel = None
default_limit:int = DEFAULT_LIMIT
table_name:str = None
def __init__(self, app:Application, table_name:str, model_class:BaseModel):
def __init__(self, app):
self.app = app
if not self.model_class:
raise ValueError("Mapper configuration error: model_class is not set.")
self.model_class = model_class
self.table_name = table_name
if not self.table_name:
raise ValueError("Mapper configuration error: table_name is not set.")
self.default_limit = self.__class__.default_limit
@property
@ -33,12 +27,12 @@ class Mapper:
def table(self):
return self.db[self.table_name]
async def get(self, uid:str=None, **kwargs) -> types.Optional[BaseModel]
async def get(self, uid:str=None, **kwargs) -> BaseModel:
if uid:
kwargs['uid'] = uid
model = self.new()
record = self.table.find_one(**kwargs)
return self.model_class.from_record(mapper=self,record=record)
return await self.model_class.from_record(mapper=self,record=record)
async def exists(self, **kwargs):
return self.table.exists(**kwargs)
@ -47,16 +41,16 @@ class Mapper:
return self.table.count(**kwargs)
async def save(self, model:BaseModel) -> bool:
record = model.record
record = await model.record
if not record.get('uid'):
raise Exception(f"Attempt to save without uid: {record}.")
return self.table.upsert(record,['uid'])
async def find(self, **kwargs) -> types.List[BaseModel]:
async def find(self, **kwargs) -> typing.AsyncGenerator:
if not kwargs.get("_limit"):
kwargs["_limit"] = self.default_limit
for record in self.table.find(**kwargs):
yield self.model_class.from_record(mapper=self,record=record)
yield await self.model_class.from_record(mapper=self,record=record)
async def delete(self, kwargs=None)-> int:
if not kwargs or not isinstance(kwargs, dict):

View File

@ -70,9 +70,11 @@ class Validator:
def custom_validation(self):
return True
def __init__(self, required=False, min_num=None, max_num=None, min_length=None, max_length=None, regex=None, value=None, kind=None, help_text=None, **kwargs):
def __init__(self, required=False, min_num=None, max_num=None, min_length=None, max_length=None, regex=None, value=None, kind=None, help_text=None, app=None, model=None, **kwargs):
self.index = Validator._index
Validator._index += 1
self.app = app
self.model = model
self.required = required
self.min_num = min_num
self.max_num = max_num
@ -86,7 +88,7 @@ class Validator:
self.__dict__.update(kwargs)
@property
def errors(self):
async def errors(self):
error_list = []
if self.value is None and self.required:
error_list.append("Field is required.")
@ -110,20 +112,23 @@ class Validator:
error_list.append(f"Invalid kind. It is supposed to be {self.kind}.")
return error_list
def validate(self):
if self.errors:
raise ValueError("\n", self.errors)
async def validate(self):
errors = await self.errors
if errors:
raise ValueError(f"Errors: {errors}.")
return True
@property
def is_valid(self):
async def is_valid(self):
try:
self.validate()
await self.validate()
return True
except ValueError:
return False
def to_json(self):
async def to_json(self):
errors = await self.errors
is_valid = await self.is_valid
return {
"required": self.required,
"min_num": self.min_num,
@ -134,8 +139,8 @@ class Validator:
"value": self.value,
"kind": str(self.kind),
"help_text": self.help_text,
"errors": self.errors,
"is_valid": self.is_valid,
"errors": errors,
"is_valid": is_valid,
"index": self.index
}
@ -149,8 +154,8 @@ class ModelField(Validator):
self.save = save
super().__init__(*args, **kwargs)
def to_json(self):
result = super().to_json()
async def to_json(self):
result = await super().to_json()
result['name'] = self.name
return result
@ -193,7 +198,7 @@ class BaseModel:
deleted_at = DeletedField(name="deleted_at", regex=TIMESTAMP_REGEX, place_holder="Deleted at")
@classmethod
def from_record(cls, record, mapper):
async def from_record(cls, record, mapper):
model = cls.__new__()
model.mapper = mapper
model.record = record
@ -230,6 +235,8 @@ class BaseModel:
self.__dict__[key] = copy.deepcopy(obj)
self.__dict__[key].value = kwargs.pop(key, self.__dict__[key].initial_value)
self.fields[key] = self.__dict__[key]
self.fields[key].model = self
self.fields[key].app = kwargs.get('app')
def __setitem__(self, key, value):
obj = self.__dict__.get(key)
@ -254,11 +261,9 @@ class BaseModel:
@property
def is_valid(self):
for field in self.fields.values():
if not field.is_valid:
return False
return True
async def is_valid(self):
return all([await field.is_valid for field in self.fields.values()])
def __getitem__(self, key):
obj = self.__dict__.get(key)
@ -273,28 +278,31 @@ class BaseModel:
self.__dict__[key] = value
@property
def record(self):
obj = self.to_json()
async def record(self):
obj = await self.to_json()
record = {}
for key, value in obj.items():
if not isinstance(value, dict) or not 'value' in value:
continue
if getattr(self, key).save:
record[key] = value.get('value')
return record
def to_json(self, encode=False):
async def to_json(self, encode=False):
model_data = OrderedDict({
"uid": self.uid.value,
"created_at": self.created_at.value,
"updated_at": self.updated_at.value,
"deleted_at": self.deleted_at.value
"deleted_at": self.deleted_at.value,
"is_valid": await self.is_valid
})
for key, value in self.__dict__.items():
for key, value in self.fields.items():
if key == "record":
continue
value = self.__dict__[key]
if hasattr(value, "value"):
model_data[key] = value.to_json()
model_data[key] = await value.to_json()
if encode:
return json.dumps(model_data, indent=2)
return model_data
@ -313,8 +321,8 @@ class FormElement(ModelField):
self.place_holder = place_holder
super().__init__(*args, **kwargs)
def to_json(self):
data = super().to_json()
async def to_json(self):
data = await super().to_json()
data["name"] = self.name
data["place_holder"] = self.place_holder
return data

View File

@ -17,10 +17,10 @@ class BaseService:
self.mapper = None
async def exists(self, **kwargs):
return self.mapper.exists(**kwargs)
return await self.count(**kwargs) > 0
async def count(self, **kwargs):
return self.mapper.count(**kwargs)
return await self.mapper.count(**kwargs)
async def new(self, **kwargs):
return await self.mapper.new()
@ -29,9 +29,9 @@ class BaseService:
return await self.mapper.get(**kwargs)
async def save(self, model:UserModel):
if model.is_valid:
return self.mapper.save(model) and True
return False
# if model.is_valid: You Know why not
return await self.mapper.save(model) and True
async def find(self, **kwargs):
return await self.mapper.find(**kwargs)

View File

@ -27,12 +27,21 @@ class BaseFormView(BaseView):
form = None
async def get(self):
form = self.form()
return await self.json_response(form.to_json())
form = self.form(app=self.app)
return await self.json_response(await form.to_json())
async def post(self):
form = self.form()
form = self.form(app=self.app)
post = await self.request.json()
form.set_user_data(post['form'])
return await self.json_response(form.to_json())
result = await form.to_json()
if post.get('action') == 'validate':
# Pass
pass
if post.get('action') == 'submit' and result['is_valid']:
await self.submit(form)
return await self.json_response(result)
async def submit(self,model=None):
print("Submit sucess")

View File

@ -2,4 +2,8 @@ from snek.form.register import RegisterForm
from snek.system.view import BaseFormView
class RegisterFormView(BaseFormView):
form = RegisterForm
form = RegisterForm
async def submit(self, form):
result = await self.app.services.user.register(form.email.value,form.username.value,form.password.value)
print("SUBMITTED:",result)