Update Models and Schema
This commit is contained in:
108
migrations/versions/938218f911e8_.py
Normal file
108
migrations/versions/938218f911e8_.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 938218f911e8
|
||||||
|
Revises:
|
||||||
|
Create Date: 2022-07-12 06:31:18.835124
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '938218f911e8'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('access_keys',
|
||||||
|
sa.Column('key', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('clientId', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('clientIP', sa.String(length=46), nullable=False),
|
||||||
|
sa.Column('userAgent', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('suspended', sa.Boolean(), nullable=False),
|
||||||
|
sa.Column('revoked', sa.Boolean(), nullable=False),
|
||||||
|
sa.Column('createdAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('suspendedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('revokedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('restoredAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('key')
|
||||||
|
)
|
||||||
|
op.create_table('access_tokens',
|
||||||
|
sa.Column('token', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('clientIP', sa.String(length=46), nullable=False),
|
||||||
|
sa.Column('userAgent', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('expired', sa.Boolean(), nullable=False),
|
||||||
|
sa.Column('revoked', sa.Boolean(), nullable=False),
|
||||||
|
sa.Column('issuedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('expiresAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('revokedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('token')
|
||||||
|
)
|
||||||
|
op.create_table('lists',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('accessKey', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('notes', sa.String(), nullable=True),
|
||||||
|
sa.Column('createdAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('modifiedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('products',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('accessKey', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('category', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('notes', sa.String(), nullable=True),
|
||||||
|
sa.Column('createdAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('modifiedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_products_category'), 'products', ['category'], unique=False)
|
||||||
|
op.create_table('sigl_config',
|
||||||
|
sa.Column('key', sa.String(), nullable=False),
|
||||||
|
sa.Column('value', sa.String(length=128), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('key')
|
||||||
|
)
|
||||||
|
op.create_table('list_entries',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('list_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('quantity', sa.String(length=128), nullable=True),
|
||||||
|
sa.Column('crossedOff', sa.Boolean(), nullable=True),
|
||||||
|
sa.Column('deleted', sa.Boolean(), nullable=True),
|
||||||
|
sa.Column('notes', sa.String(), nullable=True),
|
||||||
|
sa.Column('createdAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('modifiedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['list_id'], ['lists.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('product_locations',
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('store', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('aisle', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('bin', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('notes', sa.String(), nullable=True),
|
||||||
|
sa.Column('createdAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('modifiedAt', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('product_id', 'store')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('product_locations')
|
||||||
|
op.drop_table('list_entries')
|
||||||
|
op.drop_table('sigl_config')
|
||||||
|
op.drop_index(op.f('ix_products_category'), table_name='products')
|
||||||
|
op.drop_table('products')
|
||||||
|
op.drop_table('lists')
|
||||||
|
op.drop_table('access_tokens')
|
||||||
|
op.drop_table('access_keys')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
"""empty message
|
|
||||||
|
|
||||||
Revision ID: b3639db87e06
|
|
||||||
Revises:
|
|
||||||
Create Date: 2022-07-11 17:09:20.502213
|
|
||||||
|
|
||||||
"""
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = 'b3639db87e06'
|
|
||||||
down_revision = None
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.create_table('products',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(length=128), nullable=False),
|
|
||||||
sa.Column('category', sa.String(length=128), nullable=False),
|
|
||||||
sa.Column('notes', sa.String(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('modified_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_products_category'), 'products', ['category'], unique=False)
|
|
||||||
op.create_table('sigl_config',
|
|
||||||
sa.Column('key', sa.String(), nullable=False),
|
|
||||||
sa.Column('value', sa.String(length=128), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('key')
|
|
||||||
)
|
|
||||||
op.create_table('product_locations',
|
|
||||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('store', sa.String(length=128), nullable=False),
|
|
||||||
sa.Column('aisle', sa.String(length=64), nullable=False),
|
|
||||||
sa.Column('bin', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('notes', sa.String(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('modified_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('product_id', 'store')
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table('product_locations')
|
|
||||||
op.drop_table('sigl_config')
|
|
||||||
op.drop_index(op.f('ix_products_category'), table_name='products')
|
|
||||||
op.drop_table('products')
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
@@ -15,6 +15,91 @@ sigl_config = db.Table(
|
|||||||
db.Column('value', db.String(128)),
|
db.Column('value', db.String(128)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#: Access Key Table
|
||||||
|
access_keys = db.Table(
|
||||||
|
'access_keys',
|
||||||
|
|
||||||
|
# Primary Key
|
||||||
|
db.Column('key', db.String(64), primary_key=True),
|
||||||
|
|
||||||
|
# Client Attributes
|
||||||
|
db.Column('clientId', db.String(64), nullable=False),
|
||||||
|
db.Column('clientIP', db.String(46), nullable=False),
|
||||||
|
db.Column('userAgent', db.String(255), nullable=False),
|
||||||
|
|
||||||
|
# Key Validity
|
||||||
|
db.Column('suspended', db.Boolean, nullable=False, default=False),
|
||||||
|
db.Column('revoked', db.Boolean, nullable=False, default=False),
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
db.Column('createdAt', db.DateTime(), default=None),
|
||||||
|
db.Column('suspendedAt', db.DateTime(), default=None),
|
||||||
|
db.Column('revokedAt', db.DateTime(), default=None),
|
||||||
|
db.Column('restoredAt', db.DateTime(), default=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Access Token Table
|
||||||
|
access_tokens = db.Table(
|
||||||
|
'access_tokens',
|
||||||
|
|
||||||
|
# Primary Key
|
||||||
|
db.Column('token', db.String(64), primary_key=True),
|
||||||
|
|
||||||
|
# Client Attributes
|
||||||
|
db.Column('clientIP', db.String(46), nullable=False),
|
||||||
|
db.Column('userAgent', db.String(255), nullable=False),
|
||||||
|
|
||||||
|
# Key Validity
|
||||||
|
db.Column('expired', db.Boolean, nullable=False, default=False),
|
||||||
|
db.Column('revoked', db.Boolean, nullable=False, default=False),
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
db.Column('issuedAt', db.DateTime(), default=None),
|
||||||
|
db.Column('expiresAt', db.DateTime(), default=None),
|
||||||
|
db.Column('revokedAt', db.DateTime(), default=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Shopping List Table
|
||||||
|
lists = db.Table(
|
||||||
|
'lists',
|
||||||
|
|
||||||
|
# Primary Key
|
||||||
|
db.Column('id', db.Integer, primary_key=True),
|
||||||
|
|
||||||
|
# Access Key
|
||||||
|
db.Column('accessKey', db.String(64), default=None),
|
||||||
|
|
||||||
|
# List Attributes
|
||||||
|
db.Column('name', db.String(128), nullable=False),
|
||||||
|
|
||||||
|
# Mixin Columns
|
||||||
|
db.Column('notes', db.String(), default=None),
|
||||||
|
db.Column('createdAt', db.DateTime(), default=None),
|
||||||
|
db.Column('modifiedAt', db.DateTime(), default=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
#: List Entry Table
|
||||||
|
list_entries = db.Table(
|
||||||
|
'list_entries',
|
||||||
|
|
||||||
|
# Primary Key
|
||||||
|
db.Column('id', db.Integer, primary_key=True),
|
||||||
|
|
||||||
|
# Shopping List and Product Link
|
||||||
|
db.Column('list_id', db.ForeignKey('lists.id'), nullable=False),
|
||||||
|
db.Column('product_id', db.ForeignKey('products.id'), nullable=False),
|
||||||
|
|
||||||
|
# Entry Attributes
|
||||||
|
db.Column('quantity', db.String(128), default=None),
|
||||||
|
db.Column('crossedOff', db.Boolean, default=False),
|
||||||
|
db.Column('deleted', db.Boolean, default=False),
|
||||||
|
|
||||||
|
# Mixin Columns
|
||||||
|
db.Column('notes', db.String(), default=None),
|
||||||
|
db.Column('createdAt', db.DateTime(), default=None),
|
||||||
|
db.Column('modifiedAt', db.DateTime(), default=None),
|
||||||
|
)
|
||||||
|
|
||||||
#: Product Table
|
#: Product Table
|
||||||
products = db.Table(
|
products = db.Table(
|
||||||
'products',
|
'products',
|
||||||
@@ -22,14 +107,17 @@ products = db.Table(
|
|||||||
# Primary Key
|
# Primary Key
|
||||||
db.Column('id', db.Integer, primary_key=True),
|
db.Column('id', db.Integer, primary_key=True),
|
||||||
|
|
||||||
|
# Access Key
|
||||||
|
db.Column('accessKey', db.String(64), default=None),
|
||||||
|
|
||||||
# Product Attributes
|
# Product Attributes
|
||||||
db.Column('name', db.String(128), nullable=False),
|
db.Column('name', db.String(128), nullable=False),
|
||||||
db.Column('category', db.String(128), nullable=False, index=True),
|
db.Column('category', db.String(128), nullable=False, index=True),
|
||||||
|
|
||||||
# Mixin Columns
|
# Mixin Columns
|
||||||
db.Column('notes', db.String(), default=None),
|
db.Column('notes', db.String(), default=None),
|
||||||
db.Column('created_at', db.DateTime(), default=None),
|
db.Column('createdAt', db.DateTime(), default=None),
|
||||||
db.Column('modified_at', db.DateTime(), default=None),
|
db.Column('modifiedAt', db.DateTime(), default=None),
|
||||||
)
|
)
|
||||||
|
|
||||||
#: Product Location Table
|
#: Product Location Table
|
||||||
@@ -51,6 +139,6 @@ product_locations = db.Table(
|
|||||||
|
|
||||||
# Mixin Columns
|
# Mixin Columns
|
||||||
db.Column('notes', db.String(), default=None),
|
db.Column('notes', db.String(), default=None),
|
||||||
db.Column('created_at', db.DateTime(), default=None),
|
db.Column('createdAt', db.DateTime(), default=None),
|
||||||
db.Column('modified_at', db.DateTime(), default=None),
|
db.Column('modifiedAt', db.DateTime(), default=None),
|
||||||
)
|
)
|
||||||
|
|||||||
67
sigl/domain/models/accessKey.py
Normal file
67
sigl/domain/models/accessKey.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"""Sigl Access Key Model.
|
||||||
|
|
||||||
|
Simple Grocery List (Sigl) | sigl.app
|
||||||
|
Copyright (c) 2022 Asymworks, LLC. All Rights Reserved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
__all__ = ('AccessKey', 'AccessToken')
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AccessKey:
|
||||||
|
"""Sigl Access Key Class.
|
||||||
|
|
||||||
|
The Access Key represents a client or group of clients authorized to
|
||||||
|
interact with their specific Sigl shopping lists and products. Access Keys
|
||||||
|
are generally not revoked or deleted and remain for the lifetime of the
|
||||||
|
server.
|
||||||
|
|
||||||
|
When an Access Key is created, client details including the IP address and
|
||||||
|
User Agent string are logged for future auditing purposes. The IP address
|
||||||
|
may also be used for throttling.
|
||||||
|
"""
|
||||||
|
key: str = None
|
||||||
|
clientId: str = None
|
||||||
|
clientIP: str = None
|
||||||
|
userAgent: str = None
|
||||||
|
|
||||||
|
# Key Suspension/Revocation
|
||||||
|
suspended: bool = False
|
||||||
|
revoked: bool = False
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
createdAt: datetime = datetime.utcnow()
|
||||||
|
suspendedAt: datetime = None
|
||||||
|
revokedAt: datetime = None
|
||||||
|
restoredAt: datetime = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AccessToken:
|
||||||
|
"""Sigl Access Token Class.
|
||||||
|
|
||||||
|
The Access Token represents authorization for a client (identified by an
|
||||||
|
Access Key) to interact with the Sigl server. The token string, issue time,
|
||||||
|
and expiry time must match the client-provided JWT for access to be granted.
|
||||||
|
Access Tokens are short-lived tokens expected to expire or be revoked, and
|
||||||
|
new tokens requested by the client.
|
||||||
|
|
||||||
|
When an Access Key is created, client details including the IP address and
|
||||||
|
User Agent string are logged for future auditing purposes.
|
||||||
|
"""
|
||||||
|
token: str = None
|
||||||
|
accessKey: str = None
|
||||||
|
clientIP: str = None
|
||||||
|
userAgent: str = None
|
||||||
|
|
||||||
|
# Key Suspension/Revocation
|
||||||
|
expired: bool = False
|
||||||
|
revoked: bool = False
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
issuedAt: datetime = datetime.utcnow()
|
||||||
|
expiresAt: datetime = None
|
||||||
|
revokedAt: datetime = None
|
||||||
44
sigl/domain/models/list.py
Normal file
44
sigl/domain/models/list.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"""Sigl Shopping List Domain Model.
|
||||||
|
|
||||||
|
Simple Grocery List (Sigl) | sigl.app
|
||||||
|
Copyright (c) 2022 Asymworks, LLC. All Rights Reserved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .mixins import NotesMixin, TimestampMixin
|
||||||
|
from .product import Product
|
||||||
|
|
||||||
|
__all__ = ('ShoppingList', 'ListEntry')
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ListEntry(NotesMixin, TimestampMixin):
|
||||||
|
"""Information about a Product on a Shopping List.
|
||||||
|
|
||||||
|
This class contains information about a Product that is on a shopping
|
||||||
|
list, including the quantity to be purchased, notes about the entry, and
|
||||||
|
whether the Product has been crossed off the list or deleted.
|
||||||
|
"""
|
||||||
|
quantity: str = None
|
||||||
|
crossedOff: bool = False
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
# Relationship Fields
|
||||||
|
shoppingList: 'ShoppingList' = None
|
||||||
|
product: Product = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShoppingList(NotesMixin, TimestampMixin):
|
||||||
|
"""Top-Level Shopping List.
|
||||||
|
|
||||||
|
Contains a collection of `ListEntry` items which are intended to be
|
||||||
|
purchased.
|
||||||
|
"""
|
||||||
|
accessKey: str = None
|
||||||
|
name: str = None
|
||||||
|
|
||||||
|
# Populated by ORM
|
||||||
|
# entries: List[ListEntry] = None
|
||||||
@@ -34,8 +34,8 @@ class TimestampMixin:
|
|||||||
Adds a ``modified_at`` column to the database object with type
|
Adds a ``modified_at`` column to the database object with type
|
||||||
:py:class:`datetime.datetime`
|
:py:class:`datetime.datetime`
|
||||||
'''
|
'''
|
||||||
created_at: datetime = datetime.utcnow()
|
createdAt: datetime = datetime.utcnow()
|
||||||
modified_at: datetime = None
|
modifiedAt: datetime = None
|
||||||
|
|
||||||
def set_modified_at(self):
|
def set_modified_at(self):
|
||||||
self.modified_at = datetime.utcnow()
|
self.modifiedAt = datetime.utcnow()
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class Product(NotesMixin, TimestampMixin):
|
|||||||
name: str = None
|
name: str = None
|
||||||
category: str = None
|
category: str = None
|
||||||
|
|
||||||
|
# Access Control
|
||||||
|
accessKey: str = None
|
||||||
|
|
||||||
# Populated by ORM
|
# Populated by ORM
|
||||||
# locations: List['ProductLocation'] = []
|
# locations: List['ProductLocation'] = []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user