Browse Source

Working on CLI app.

Cixo Develop 6 months ago
parent
commit
4e9afdfa84
7 changed files with 267 additions and 9 deletions
  1. 10 1
      assets/__init__.py
  2. 11 2
      assets/config.py
  3. 20 2
      assets/user.py
  4. 42 2
      assets/users_collection.py
  5. 2 2
      assets/users_saver.py
  6. 181 0
      core.py
  7. 1 0
      requirements.txt

+ 10 - 1
assets/__init__.py

@@ -10,6 +10,14 @@ from .validator import apikey_validator
 
 
 """ Exceptions. """
 """ Exceptions. """
 from .exception import validator_exception
 from .exception import validator_exception
+from .exception import config_exception
+from .exception import exists_exception
+from .exception import bad_request_exception
+from .exception import not_found_exception
+from .exception import not_ready_exception
+from .exception import in_collection_exception
+from .exception import access_denied_exception
+from .exception import incomplete_request_exception
 
 
 """ Product and helpers. """
 """ Product and helpers. """
 from .product import product
 from .product import product
@@ -31,6 +39,7 @@ from .user import user_exporter
 """ User loaders and collections. """
 """ User loaders and collections. """
 from .users_collection import users_collection
 from .users_collection import users_collection
 from .users_loader import users_loader
 from .users_loader import users_loader
+from .users_saver import users_saver
 
 
 """ Configs and loaders. """
 """ Configs and loaders. """
 from .config import config
 from .config import config
@@ -39,4 +48,4 @@ from .config import config_generator
 
 
 """ App config and resources. """
 """ App config and resources. """
 from .app_config import app_config
 from .app_config import app_config
-from .app_resources import app_resources
+from .app_resources import app_resources

+ 11 - 2
assets/config.py

@@ -66,9 +66,18 @@ class config_generator:
             raise TypeError("Config is skeleton class for configs.")
             raise TypeError("Config is skeleton class for configs.")
 
 
         self.__config = target().defaults
         self.__config = target().defaults
-        self.__json_config = json.dumps(self.__config)
+    
+    def modify(self, key: str, value: str | int | float) -> object:
+        if not key in self.__config:
+            raise config_exception("Key \"" + key + "\" not exists.")
+
+        self.__config[key] = value 
+
+        return self
 
 
     def save(self, where: pathlib.Path) -> None:
     def save(self, where: pathlib.Path) -> None:
+        json_config = json.dumps(self.__config)
+
         if where.exists():
         if where.exists():
             content = "Can not create config file \"" + str(where) + "\" "
             content = "Can not create config file \"" + str(where) + "\" "
             content = content + "because it already exists in filesystem."
             content = content + "because it already exists in filesystem."
@@ -76,7 +85,7 @@ class config_generator:
             raise config_exception(content)
             raise config_exception(content)
 
 
         with where.open("w") as handler:
         with where.open("w") as handler:
-            handler.write(self.__json_config)
+            handler.write(json_config)
 
 
 class config_loader:
 class config_loader:
     def __init__(self, target: type) -> None:
     def __init__(self, target: type) -> None:

+ 20 - 2
assets/user.py

@@ -26,7 +26,6 @@ class user:
         self.__nick = nick
         self.__nick = nick
         self.__password = password
         self.__password = password
         self.__apikey = apikey
         self.__apikey = apikey
-        self.__result = None
 
 
     @property
     @property
     def nick(self) -> str:
     def nick(self) -> str:
@@ -46,6 +45,14 @@ class user:
 
 
         return self.__apikey
         return self.__apikey
 
 
+    @property
+    def flat(self) -> dict:
+        return {
+            "nick": self.nick,
+            "password": self.password,
+            "apikey": self.apikey
+        }
+
     def __str__(self) -> str:
     def __str__(self) -> str:
         """ 
         """ 
         This create dump of the user.
         This create dump of the user.
@@ -95,6 +102,17 @@ class user_factory:
 
 
         return self.__apikey
         return self.__apikey
 
 
+    def refresh_apikey(self) -> object:
+        """
+        This function drop current ApiKey to logout user.
+        
+        Returns:
+            (object): Instance itself
+        """
+
+        self.__apikey = None
+        return self
+
     @property
     @property
     def nick(self) -> str | None:
     def nick(self) -> str | None:
         """ This return nick of the factory. """
         """ This return nick of the factory. """
@@ -280,4 +298,4 @@ class user_exporter:
             "nick": self.target.nick,
             "nick": self.target.nick,
             "password": self.target.password,
             "password": self.target.password,
             "apikey": self.target.apikey
             "apikey": self.target.apikey
-        }
+        }

+ 42 - 2
assets/users_collection.py

@@ -17,6 +17,36 @@ class users_collection:
         self.__by_apikey = dict()
         self.__by_apikey = dict()
         self.__by_nick = dict()
         self.__by_nick = dict()
 
 
+    def get_by_nick(self, nick: str) -> user | None:
+        """
+        This function return user by nick.
+
+        Parameters:
+            nick (str): Nick of the user to get
+
+        Returns:
+            (user | None): User with that nick, or None
+        """
+
+        if self.exists(nick):
+            return self.__by_nick[nick]
+
+        return None
+
+    def exists(self, nick: str) -> bool:
+        """
+        This function check that user with given nick exists in the 
+        collection.
+
+        Parameters:
+            nick (str): Nick of the user to check
+
+        Returns:    
+            (bool): True when user exists, or False if not
+        """
+
+        return nick in self.__by_nick
+    
     @property
     @property
     def all(self) -> list:
     def all(self) -> list:
         """
         """
@@ -26,13 +56,16 @@ class users_collection:
 
 
         return list(self.__by_apikey.values())
         return list(self.__by_apikey.values())
     
     
-    def add(self, target: user) -> None:
+    def add(self, target: user) -> object:
         """
         """
         This add new user to the collection. When ApiKey or login already
         This add new user to the collection. When ApiKey or login already
         exists in the collection, then error had been raised.
         exists in the collection, then error had been raised.
 
 
         Parameters:
         Parameters:
             target (user): New user to add
             target (user): New user to add
+
+        Returns:
+            (object): This object itself
         """
         """
 
 
         if target.apikey in self.__by_apikey:
         if target.apikey in self.__by_apikey:
@@ -50,6 +83,8 @@ class users_collection:
         self.__by_apikey[target.apikey] = target
         self.__by_apikey[target.apikey] = target
         self.__by_nick[target.nick] = target
         self.__by_nick[target.nick] = target
 
 
+        return self
+
     def login(self, nick: str, password: str) -> user | None:
     def login(self, nick: str, password: str) -> user | None:
         """
         """
         This try to login user by nick and password. When nick or password 
         This try to login user by nick and password. When nick or password 
@@ -75,12 +110,15 @@ class users_collection:
 
 
         return target
         return target
 
 
-    def remove(self, target: user) -> None:
+    def remove(self, target: user) -> object:
         """
         """
         This function remove target user from collection.
         This function remove target user from collection.
 
 
         Parameters:
         Parameters:
             target (user): Target user to remove from collection
             target (user): Target user to remove from collection
+
+        Returns:
+            (object): This object itself
         """
         """
 
 
         exists = target.nick in self.__by_nick
         exists = target.nick in self.__by_nick
@@ -98,6 +136,8 @@ class users_collection:
         self.__by_nick = by_nick
         self.__by_nick = by_nick
         self.__by_apikey = by_apikey
         self.__by_apikey = by_apikey
 
 
+        return self
+
     def get(self, apikey: str) -> user | None:
     def get(self, apikey: str) -> user | None:
         """
         """
         This try to load user by ApiKey. When user with that ApiKey exists
         This try to load user by ApiKey. When user with that ApiKey exists

+ 2 - 2
assets/users_saver.py

@@ -27,7 +27,7 @@ class users_saver:
     def users(self) -> list:
     def users(self) -> list:
         """ This return list of users in setup collection. """
         """ This return list of users in setup collection. """
 
 
-        return self.__collection.all
+        return [ count.flat for count in self.__collection.all ]
 
 
     def drop(self, where: pathlib.Path) -> object:
     def drop(self, where: pathlib.Path) -> object:
         """
         """
@@ -77,7 +77,7 @@ class users_saver:
             raise RuntimeError("Where is not set.")
             raise RuntimeError("Where is not set.")
 
 
         if where.exists():
         if where.exists():
-            content = "Config file \"" + str(where) + "\" already exists.")
+            content = "Config file \"" + str(where) + "\" already exists."
             
             
             raise config_exception(content)
             raise config_exception(content)
 
 

+ 181 - 0
core.py

@@ -0,0 +1,181 @@
+import typer
+import enum
+import pathlib
+import getpass
+import json
+
+from assets import *
+
+default_config = pathlib.Path("config.json")
+default_users_db = pathlib.Path("users.json")
+default_db = pathlib.Path("database.db")
+
+config_help = "This is configuration file of the app to use."
+users_db_help = "This is location of the users json database."
+db_help = "This is location of SQLite3 database file."
+
+description = "This is core reservationer package. It could be used to host "
+description = description + "app, or manage configuration or database."
+
+app = typer.Typer(help = description)
+
+class user_command(str, enum.Enum):
+    """
+    That commands could be used in the user subcommand.
+    """
+
+    add = "register"
+    delete = "delete"
+    password = "password-change"
+    logout = "full-logout"
+
+def password_prompt() -> str:
+    while True: 
+        first = getpass.getpass("Password: ")
+        second = getpass.getpass("Repeat password: ")
+
+        if first == second:
+            return first
+
+        print("Passwords do not match.")
+
[email protected]()
+def server(
+    port: int = typer.Option(8080, help = "Port to listen on."),
+    address: str = typer.Option("0.0.0.0", help = "Address to listen on."),
+    config: pathlib.Path = typer.Option(default_config, help = config_help)
+) -> None:
+    """
+    Start app on selected port and interfaces.
+    """
+
+    print(port)
+    print(address)
+
[email protected]()
+def user(
+    command: user_command = typer.Argument(help = "Command to run on users."),
+    nick: str = typer.Argument(help = "Nick of the user to work on."),
+    config: pathlib.Path = typer.Option(default_config, help = config_help)
+) -> None:
+    """
+    Modify user database.
+    """
+    
+    try:
+        loader = config_loader(app_config).load(config)
+        collection = loader.resources.users
+
+        # User adding 
+        if command == user_command.add:
+            
+            # Check that user exists
+            if collection.exists(nick):
+                raise Exception("User with that nick already exists.")
+
+            # Create new user
+            creator = user_factory()
+            creator.nick = nick
+            creator.password = password_prompt()
+            
+            # Add it
+            collection.add(creator.result)
+            
+            print("Adding \"" + nick + "\" to the database.")
+
+        # User remove
+        elif command == user_command.delete:
+            
+            # Load from database
+            target = collection.get_by_nick(nick)
+
+            # Check that user exists
+            if target is None:
+                raise Exception("User with given nick not exists.")
+            
+            # When exists remove it
+            collection.remove(target)
+
+        # Change user password
+        elif command == user_command.password:
+                
+            # Load user by nick 
+            target = collection.get_by_nick(nick)
+            
+            # Check that user exists
+            if target is None:
+                raise Exception("User not exists, can not change password.")
+
+            # Change password
+            handler = user_factory(target)
+            handler.password = password_prompt()
+            modified = handler.result
+            
+            # Store it in collection
+            collection.remove(target).add(modified)
+        
+        # Logout user from all devices
+        elif command == user_command.logout:
+            
+            # Load user from database
+            target = collection.get_by_nick(nick)
+            
+            # Check that exists
+            if target is None:
+                raise Exception("User not exists, can not logout.")
+
+            # Refresh apikey
+            modified = user_factory(target) \
+            .refresh_apikey() \
+            .result
+            
+            # Store result
+            collection.remove(target).add(modified)
+
+        # Save collection to file
+        users_saver(collection) \
+        .drop(loader.result.users_path) \
+        .save()
+
+        print("Users database saved success.")
+
+    except validator_exception as error:
+        print("Password is not correct, too easy to break.")
+
+    except json.JSONDecodeError as error:
+        print("User JSON has syntax exception.")
+        print(str(error))
+
+    except Exception as error:
+        print("Can not done work.")
+        print(str(error))
+
[email protected]()
+def initialize(
+    config: pathlib.Path = typer.Option(default_config, help = config_help),
+    users: pathlib.Path = typer.Option(default_users_db, help = users_db_help),
+    database: pathlib.Path = typer.Option(default_db, help = db_help)
+) -> None:
+    """
+    Initialize app configuration.
+    """
+
+    try:
+        
+        # Generating config file
+        config_generator(app_config) \
+        .modify("users_file", str(users)) \
+        .modify("database_uri", "sqlite:///" + str(database)) \
+        .save(config)
+        
+        # Generating new blank users database
+        users_saver(users_collection()).save(users)
+        
+        print("Config file is being created.")
+    
+    except Exception as error:
+        print("Config initialization failed.")
+        print(str(error))
+
+if __name__ == "__main__":
+    app()

+ 1 - 0
requirements.txt

@@ -1,2 +1,3 @@
 sqlmodel
 sqlmodel
 fastapi
 fastapi
+typer-slim