├── .flake8 ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── .vscode └── launch.json ├── requirements.txt ├── run.py ├── src ├── __init__.py ├── data │ ├── __init__.py │ ├── find_pet │ │ ├── __init__.py │ │ ├── find.py │ │ └── find_test.py │ ├── find_user │ │ ├── __init__.py │ │ ├── find.py │ │ └── find_test.py │ ├── interfaces │ │ ├── __init__.py │ │ ├── pet_repository_interface.py │ │ └── user_repository_interface.py │ ├── register_pet │ │ ├── __init__.py │ │ ├── register.py │ │ └── register_test.py │ ├── register_user │ │ ├── __init__.py │ │ ├── register.py │ │ └── register_test.py │ └── test │ │ ├── __init__.py │ │ ├── find_pet_spy.py │ │ ├── find_user_spy.py │ │ ├── register_pet_spy.py │ │ └── register_user_spy.py ├── domain │ ├── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── pets.py │ │ └── users.py │ ├── test │ │ ├── __init__.py │ │ ├── mock_pet.py │ │ └── mock_user.py │ └── use_cases │ │ ├── __init__.py │ │ ├── find_pet.py │ │ ├── find_user.py │ │ ├── register_pet.py │ │ └── register_user.py ├── infra │ ├── __init__.py │ ├── configs │ │ ├── __init__.py │ │ ├── db_base.py │ │ └── db_config.py │ ├── entities │ │ ├── __init__.py │ │ ├── pets.py │ │ └── users.py │ ├── repositorys │ │ ├── __init__.py │ │ ├── pet_repository.py │ │ ├── pet_repository_test.py │ │ ├── user_repository.py │ │ └── user_repository_test.py │ └── test │ │ ├── __init__.py │ │ ├── pet_repository_spy.py │ │ └── user_repository_spy.py ├── main │ ├── __init__.py │ ├── adapter │ │ ├── __init__.py │ │ └── router_adapter.py │ ├── composer │ │ ├── __init__.py │ │ ├── find_pet_composite.py │ │ ├── find_user_composite.py │ │ ├── register_pet_composite.py │ │ └── register_user_composite.py │ ├── config │ │ ├── __init__.py │ │ └── app.py │ ├── interface │ │ ├── __init__.py │ │ └── route.py │ └── routes │ │ ├── __init__.py │ │ └── api_routes.py └── presentation │ ├── __init__.py │ ├── controllers │ ├── __init__.py │ ├── find_pet_controller.py │ ├── find_pet_controller_test.py │ ├── find_user_controller.py │ ├── find_user_controller_test.py │ ├── register_pet_controller.py │ ├── register_pet_controller_test.py │ ├── register_user_controller.py │ └── register_user_controller_test.py │ ├── errors │ ├── __init__.py │ └── http_errors.py │ └── helpers │ ├── __init__.py │ └── http_models.py └── storage.db /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E722, W503 3 | max-line-length = 120 4 | per-file-ignores = 5 | __init__.py: F401 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | .pytest_cache 3 | venv 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/ambv/black 3 | rev: stable 4 | hooks: 5 | - id: black 6 | language_version: python3.8 7 | stages: [commit] 8 | - repo: https://gitlab.com/pycqa/flake8 9 | rev: 3.7.9 10 | hooks: 11 | - id: flake8 12 | stages: [commit] 13 | - repo: local 14 | hooks: 15 | - id: pytest 16 | name: pytest 17 | language: system 18 | entry: bash -c 'venv/bin/pytest -v -s; git add storage.db' 19 | always_run: true 20 | pass_filenames: false 21 | stages: [commit] 22 | - repo: local 23 | hooks: 24 | - id: requirements 25 | name: requirements 26 | entry: bash -c 'venv/bin/pip3 freeze > requirements.txt; git add requirements.txt' 27 | language: system 28 | pass_filenames: false 29 | stages: [commit] 30 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | disable= 4 | C0114, # missing-module-docstring 5 | 6 | # A comma-separated list of package or module names from where C extensions may 7 | # be loaded. Extensions are loading into the active Python interpreter and may 8 | # run arbitrary code. 9 | extension-pkg-whitelist= 10 | 11 | # Specify a score threshold to be exceeded before program exits with error. 12 | fail-under=10.0 13 | 14 | # Add files or directories to the blacklist. They should be base names, not 15 | # paths. 16 | ignore=CVS 17 | 18 | # Add files or directories matching the regex patterns to the blacklist. The 19 | # regex matches against base names, not paths. 20 | ignore-patterns= 21 | 22 | # Python code to execute, usually for sys.path manipulation such as 23 | # pygtk.require(). 24 | #init-hook= 25 | 26 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 27 | # number of processors available to use. 28 | jobs=1 29 | 30 | # Control the amount of potential inferred values when inferring a single 31 | # object. This can help the performance when dealing with large functions or 32 | # complex, nested conditions. 33 | limit-inference-results=100 34 | 35 | # List of plugins (as comma separated values of python module names) to load, 36 | # usually to register additional checkers. 37 | load-plugins= 38 | 39 | # Pickle collected data for later comparisons. 40 | persistent=yes 41 | 42 | # When enabled, pylint would attempt to guess common misconfiguration and emit 43 | # user-friendly hints instead of false-positive error messages. 44 | suggestion-mode=yes 45 | 46 | # Allow loading of arbitrary C extensions. Extensions are imported into the 47 | # active Python interpreter and may run arbitrary code. 48 | unsafe-load-any-extension=no 49 | 50 | 51 | [MESSAGES CONTROL] 52 | 53 | # Only show warnings with the listed confidence levels. Leave empty to show 54 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 55 | confidence= 56 | 57 | # Disable the message, report, category or checker with the given id(s). You 58 | # can either give multiple identifiers separated by comma (,) or put this 59 | # option multiple times (only on the command line, not in the configuration 60 | # file where it should appear only once). You can also use "--disable=all" to 61 | # disable everything first and then reenable specific checks. For example, if 62 | # you want to run only the similarities checker, you can use "--disable=all 63 | # --enable=similarities". If you want to run only the classes checker, but have 64 | # no Warning level messages displayed, use "--disable=all --enable=classes 65 | # --disable=W". 66 | disable=print-statement, 67 | parameter-unpacking, 68 | unpacking-in-except, 69 | old-raise-syntax, 70 | backtick, 71 | long-suffix, 72 | old-ne-operator, 73 | old-octal-literal, 74 | import-star-module-level, 75 | non-ascii-bytes-literal, 76 | raw-checker-failed, 77 | bad-inline-option, 78 | locally-disabled, 79 | file-ignored, 80 | suppressed-message, 81 | useless-suppression, 82 | deprecated-pragma, 83 | use-symbolic-message-instead, 84 | apply-builtin, 85 | basestring-builtin, 86 | buffer-builtin, 87 | cmp-builtin, 88 | coerce-builtin, 89 | execfile-builtin, 90 | file-builtin, 91 | long-builtin, 92 | raw_input-builtin, 93 | reduce-builtin, 94 | standarderror-builtin, 95 | unicode-builtin, 96 | xrange-builtin, 97 | coerce-method, 98 | delslice-method, 99 | getslice-method, 100 | setslice-method, 101 | no-absolute-import, 102 | old-division, 103 | dict-iter-method, 104 | dict-view-method, 105 | next-method-called, 106 | metaclass-assignment, 107 | indexing-exception, 108 | raising-string, 109 | reload-builtin, 110 | oct-method, 111 | hex-method, 112 | nonzero-method, 113 | cmp-method, 114 | input-builtin, 115 | round-builtin, 116 | intern-builtin, 117 | unichr-builtin, 118 | map-builtin-not-iterating, 119 | zip-builtin-not-iterating, 120 | range-builtin-not-iterating, 121 | filter-builtin-not-iterating, 122 | using-cmp-argument, 123 | eq-without-hash, 124 | div-method, 125 | idiv-method, 126 | rdiv-method, 127 | exception-message-attribute, 128 | invalid-str-codec, 129 | sys-max-int, 130 | bad-python3-import, 131 | deprecated-string-function, 132 | deprecated-str-translate-call, 133 | deprecated-itertools-function, 134 | deprecated-types-field, 135 | next-method-defined, 136 | dict-items-not-iterating, 137 | dict-keys-not-iterating, 138 | dict-values-not-iterating, 139 | deprecated-operator-function, 140 | deprecated-urllib-function, 141 | xreadlines-attribute, 142 | deprecated-sys-function, 143 | exception-escape, 144 | comprehension-escape 145 | 146 | # Enable the message, report, category or checker with the given id(s). You can 147 | # either give multiple identifier separated by comma (,) or put this option 148 | # multiple time (only on the command line, not in the configuration file where 149 | # it should appear only once). See also the "--disable" option for examples. 150 | enable=c-extension-no-member 151 | 152 | 153 | [REPORTS] 154 | 155 | # Python expression which should return a score less than or equal to 10. You 156 | # have access to the variables 'error', 'warning', 'refactor', and 'convention' 157 | # which contain the number of messages in each category, as well as 'statement' 158 | # which is the total number of statements analyzed. This score is used by the 159 | # global evaluation report (RP0004). 160 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 161 | 162 | # Template used to display messages. This is a python new-style format string 163 | # used to format the message information. See doc for all details. 164 | #msg-template= 165 | 166 | # Set the output format. Available formats are text, parseable, colorized, json 167 | # and msvs (visual studio). You can also give a reporter class, e.g. 168 | # mypackage.mymodule.MyReporterClass. 169 | output-format=text 170 | 171 | # Tells whether to display a full report or only the messages. 172 | reports=no 173 | 174 | # Activate the evaluation score. 175 | score=yes 176 | 177 | 178 | [REFACTORING] 179 | 180 | # Maximum number of nested blocks for function / method body 181 | max-nested-blocks=5 182 | 183 | # Complete name of functions that never returns. When checking for 184 | # inconsistent-return-statements if a never returning function is called then 185 | # it will be considered as an explicit return statement and no message will be 186 | # printed. 187 | never-returning-functions=sys.exit 188 | 189 | 190 | [FORMAT] 191 | 192 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 193 | expected-line-ending-format= 194 | 195 | # Regexp for a line that is allowed to be longer than the limit. 196 | ignore-long-lines=^\s*(# )??$ 197 | 198 | # Number of spaces of indent required inside a hanging or continued line. 199 | indent-after-paren=4 200 | 201 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 202 | # tab). 203 | indent-string=' ' 204 | 205 | # Maximum number of characters on a single line. 206 | max-line-length=120 207 | 208 | # Maximum number of lines in a module. 209 | max-module-lines=1000 210 | 211 | # Allow the body of a class to be on the same line as the declaration if body 212 | # contains single statement. 213 | single-line-class-stmt=no 214 | 215 | # Allow the body of an if to be on the same line as the test if there is no 216 | # else. 217 | single-line-if-stmt=no 218 | 219 | 220 | [MISCELLANEOUS] 221 | 222 | # List of note tags to take in consideration, separated by a comma. 223 | notes=FIXME, 224 | XXX, 225 | TODO 226 | 227 | # Regular expression of note tags to take in consideration. 228 | #notes-rgx= 229 | 230 | 231 | [SIMILARITIES] 232 | 233 | # Ignore comments when computing similarities. 234 | ignore-comments=yes 235 | 236 | # Ignore docstrings when computing similarities. 237 | ignore-docstrings=yes 238 | 239 | # Ignore imports when computing similarities. 240 | ignore-imports=no 241 | 242 | # Minimum lines number of a similarity. 243 | min-similarity-lines=4 244 | 245 | 246 | [STRING] 247 | 248 | # This flag controls whether inconsistent-quotes generates a warning when the 249 | # character used as a quote delimiter is used inconsistently within a module. 250 | check-quote-consistency=no 251 | 252 | # This flag controls whether the implicit-str-concat should generate a warning 253 | # on implicit string concatenation in sequences defined over several lines. 254 | check-str-concat-over-line-jumps=no 255 | 256 | 257 | [TYPECHECK] 258 | 259 | # List of decorators that produce context managers, such as 260 | # contextlib.contextmanager. Add to this list to register other decorators that 261 | # produce valid context managers. 262 | contextmanager-decorators=contextlib.contextmanager 263 | 264 | # List of members which are set dynamically and missed by pylint inference 265 | # system, and so shouldn't trigger E1101 when accessed. Python regular 266 | # expressions are accepted. 267 | generated-members= 268 | 269 | # Tells whether missing members accessed in mixin class should be ignored. A 270 | # mixin class is detected if its name ends with "mixin" (case insensitive). 271 | ignore-mixin-members=yes 272 | 273 | # Tells whether to warn about missing members when the owner of the attribute 274 | # is inferred to be None. 275 | ignore-none=yes 276 | 277 | # This flag controls whether pylint should warn about no-member and similar 278 | # checks whenever an opaque object is returned when inferring. The inference 279 | # can return multiple potential results while evaluating a Python object, but 280 | # some branches might not be evaluated, which results in partial inference. In 281 | # that case, it might be useful to still emit no-member and other checks for 282 | # the rest of the inferred objects. 283 | ignore-on-opaque-inference=yes 284 | 285 | # List of class names for which member attributes should not be checked (useful 286 | # for classes with dynamically set attributes). This supports the use of 287 | # qualified names. 288 | ignored-classes=optparse.Values,thread._local,_thread._local 289 | 290 | # List of module names for which member attributes should not be checked 291 | # (useful for modules/projects where namespaces are manipulated during runtime 292 | # and thus existing member attributes cannot be deduced by static analysis). It 293 | # supports qualified module names, as well as Unix pattern matching. 294 | ignored-modules= 295 | 296 | # Show a hint with possible names when a member name was not found. The aspect 297 | # of finding the hint is based on edit distance. 298 | missing-member-hint=yes 299 | 300 | # The minimum edit distance a name should have in order to be considered a 301 | # similar match for a missing member name. 302 | missing-member-hint-distance=1 303 | 304 | # The total number of similar names that should be taken in consideration when 305 | # showing a hint for a missing member. 306 | missing-member-max-choices=1 307 | 308 | # List of decorators that change the signature of a decorated function. 309 | signature-mutators= 310 | 311 | 312 | [BASIC] 313 | 314 | # Naming style matching correct argument names. 315 | argument-naming-style=snake_case 316 | 317 | # Regular expression matching correct argument names. Overrides argument- 318 | # naming-style. 319 | #argument-rgx= 320 | 321 | # Naming style matching correct attribute names. 322 | attr-naming-style=snake_case 323 | 324 | # Regular expression matching correct attribute names. Overrides attr-naming- 325 | # style. 326 | #attr-rgx= 327 | 328 | # Bad variable names which should always be refused, separated by a comma. 329 | bad-names=foo, 330 | bar, 331 | baz, 332 | toto, 333 | tutu, 334 | tata 335 | 336 | # Bad variable names regexes, separated by a comma. If names match any regex, 337 | # they will always be refused 338 | bad-names-rgxs= 339 | 340 | # Naming style matching correct class attribute names. 341 | class-attribute-naming-style=any 342 | 343 | # Regular expression matching correct class attribute names. Overrides class- 344 | # attribute-naming-style. 345 | #class-attribute-rgx= 346 | 347 | # Naming style matching correct class names. 348 | class-naming-style=PascalCase 349 | 350 | # Regular expression matching correct class names. Overrides class-naming- 351 | # style. 352 | #class-rgx= 353 | 354 | # Naming style matching correct constant names. 355 | const-naming-style=UPPER_CASE 356 | 357 | # Regular expression matching correct constant names. Overrides const-naming- 358 | # style. 359 | #const-rgx= 360 | 361 | # Minimum line length for functions/classes that require docstrings, shorter 362 | # ones are exempt. 363 | docstring-min-length=-1 364 | 365 | # Naming style matching correct function names. 366 | function-naming-style=snake_case 367 | 368 | # Regular expression matching correct function names. Overrides function- 369 | # naming-style. 370 | #function-rgx= 371 | 372 | # Good variable names which should always be accepted, separated by a comma. 373 | good-names=i, 374 | j, 375 | k, 376 | ex, 377 | Run, 378 | _ 379 | 380 | # Good variable names regexes, separated by a comma. If names match any regex, 381 | # they will always be accepted 382 | good-names-rgxs= 383 | 384 | # Include a hint for the correct naming format with invalid-name. 385 | include-naming-hint=no 386 | 387 | # Naming style matching correct inline iteration names. 388 | inlinevar-naming-style=any 389 | 390 | # Regular expression matching correct inline iteration names. Overrides 391 | # inlinevar-naming-style. 392 | #inlinevar-rgx= 393 | 394 | # Naming style matching correct method names. 395 | method-naming-style=snake_case 396 | 397 | # Regular expression matching correct method names. Overrides method-naming- 398 | # style. 399 | #method-rgx= 400 | 401 | # Naming style matching correct module names. 402 | module-naming-style=snake_case 403 | 404 | # Regular expression matching correct module names. Overrides module-naming- 405 | # style. 406 | #module-rgx= 407 | 408 | # Colon-delimited sets of names that determine each other's naming style when 409 | # the name regexes allow several styles. 410 | name-group= 411 | 412 | # Regular expression which should only match function or class names that do 413 | # not require a docstring. 414 | no-docstring-rgx=^_ 415 | 416 | # List of decorators that produce properties, such as abc.abstractproperty. Add 417 | # to this list to register other decorators that produce valid properties. 418 | # These decorators are taken in consideration only for invalid-name. 419 | property-classes=abc.abstractproperty 420 | 421 | # Naming style matching correct variable names. 422 | variable-naming-style=snake_case 423 | 424 | # Regular expression matching correct variable names. Overrides variable- 425 | # naming-style. 426 | #variable-rgx= 427 | 428 | 429 | [LOGGING] 430 | 431 | # The type of string formatting that logging methods do. `old` means using % 432 | # formatting, `new` is for `{}` formatting. 433 | logging-format-style=old 434 | 435 | # Logging modules to check that the string format arguments are in logging 436 | # function parameter format. 437 | logging-modules=logging 438 | 439 | 440 | [SPELLING] 441 | 442 | # Limits count of emitted suggestions for spelling mistakes. 443 | max-spelling-suggestions=4 444 | 445 | # Spelling dictionary name. Available dictionaries: none. To make it work, 446 | # install the python-enchant package. 447 | spelling-dict= 448 | 449 | # List of comma separated words that should not be checked. 450 | spelling-ignore-words= 451 | 452 | # A path to a file that contains the private dictionary; one word per line. 453 | spelling-private-dict-file= 454 | 455 | # Tells whether to store unknown words to the private dictionary (see the 456 | # --spelling-private-dict-file option) instead of raising a message. 457 | spelling-store-unknown-words=no 458 | 459 | 460 | [VARIABLES] 461 | 462 | # List of additional names supposed to be defined in builtins. Remember that 463 | # you should avoid defining new builtins when possible. 464 | additional-builtins= 465 | 466 | # Tells whether unused global variables should be treated as a violation. 467 | allow-global-unused-variables=yes 468 | 469 | # List of strings which can identify a callback function by name. A callback 470 | # name must start or end with one of those strings. 471 | callbacks=cb_, 472 | _cb 473 | 474 | # A regular expression matching the name of dummy variables (i.e. expected to 475 | # not be used). 476 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 477 | 478 | # Argument names that match this expression will be ignored. Default to name 479 | # with leading underscore. 480 | ignored-argument-names=_.*|^ignored_|^unused_ 481 | 482 | # Tells whether we should check for unused import in __init__ files. 483 | init-import=no 484 | 485 | # List of qualified module names which can have objects that can redefine 486 | # builtins. 487 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 488 | 489 | 490 | [DESIGN] 491 | 492 | # Maximum number of arguments for function / method. 493 | max-args=5 494 | 495 | # Maximum number of attributes for a class (see R0902). 496 | max-attributes=7 497 | 498 | # Maximum number of boolean expressions in an if statement (see R0916). 499 | max-bool-expr=5 500 | 501 | # Maximum number of branch for function / method body. 502 | max-branches=12 503 | 504 | # Maximum number of locals for function / method body. 505 | max-locals=15 506 | 507 | # Maximum number of parents for a class (see R0901). 508 | max-parents=7 509 | 510 | # Maximum number of public methods for a class (see R0904). 511 | max-public-methods=20 512 | 513 | # Maximum number of return / yield for function / method body. 514 | max-returns=6 515 | 516 | # Maximum number of statements in function / method body. 517 | max-statements=50 518 | 519 | # Minimum number of public methods for a class (see R0903). 520 | min-public-methods=1 521 | 522 | 523 | [CLASSES] 524 | 525 | # List of method names used to declare (i.e. assign) instance attributes. 526 | defining-attr-methods=__init__, 527 | __new__, 528 | setUp, 529 | __post_init__ 530 | 531 | # List of member names, which should be excluded from the protected access 532 | # warning. 533 | exclude-protected=_asdict, 534 | _fields, 535 | _replace, 536 | _source, 537 | _make 538 | 539 | # List of valid names for the first argument in a class method. 540 | valid-classmethod-first-arg=cls 541 | 542 | # List of valid names for the first argument in a metaclass class method. 543 | valid-metaclass-classmethod-first-arg=cls 544 | 545 | 546 | [IMPORTS] 547 | 548 | # List of modules that can be imported at any level, not just the top level 549 | # one. 550 | allow-any-import-level= 551 | 552 | # Allow wildcard imports from modules that define __all__. 553 | allow-wildcard-with-all=no 554 | 555 | # Analyse import fallback blocks. This can be used to support both Python 2 and 556 | # 3 compatible code, which means that the block might have code that exists 557 | # only in one or another interpreter, leading to false positives when analysed. 558 | analyse-fallback-blocks=no 559 | 560 | # Deprecated modules which should not be used, separated by a comma. 561 | deprecated-modules=optparse,tkinter.tix 562 | 563 | # Create a graph of external dependencies in the given file (report RP0402 must 564 | # not be disabled). 565 | ext-import-graph= 566 | 567 | # Create a graph of every (i.e. internal and external) dependencies in the 568 | # given file (report RP0402 must not be disabled). 569 | import-graph= 570 | 571 | # Create a graph of internal dependencies in the given file (report RP0402 must 572 | # not be disabled). 573 | int-import-graph= 574 | 575 | # Force import order to recognize a module as part of the standard 576 | # compatibility libraries. 577 | known-standard-library= 578 | 579 | # Force import order to recognize a module as part of a third party library. 580 | known-third-party=enchant 581 | 582 | # Couples of modules and preferred modules, separated by a comma. 583 | preferred-modules= 584 | 585 | 586 | [EXCEPTIONS] 587 | 588 | # Exceptions that will emit a warning when being caught. Defaults to 589 | # "BaseException, Exception". 590 | overgeneral-exceptions=BaseException, 591 | Exception 592 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Flask (development mode)", 9 | "type": "python", 10 | "request": "launch", 11 | "module": "flask", 12 | "env": { 13 | "FLASK_APP": "run.py", 14 | "FLASK_ENV": "development" 15 | }, 16 | "args": [ 17 | "run" 18 | ], 19 | "jinja": true 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.4 2 | astroid==2.4.2 3 | attrs==20.3.0 4 | black==20.8b1 5 | cfgv==3.2.0 6 | click==7.1.2 7 | distlib==0.3.1 8 | Faker==5.0.1 9 | filelock==3.0.12 10 | flake8==3.8.4 11 | Flask==1.1.2 12 | Flask-Cors==3.0.10 13 | freeze==3.0 14 | identify==1.5.10 15 | iniconfig==1.1.1 16 | isort==5.6.4 17 | itsdangerous==1.1.0 18 | Jinja2==2.11.3 19 | lazy-object-proxy==1.4.3 20 | MarkupSafe==1.1.1 21 | mccabe==0.6.1 22 | mypy-extensions==0.4.3 23 | nodeenv==1.5.0 24 | packaging==20.8 25 | pathspec==0.8.1 26 | pluggy==0.13.1 27 | pre-commit==2.9.3 28 | py==1.10.0 29 | pycodestyle==2.6.0 30 | pyflakes==2.2.0 31 | pylint==2.6.0 32 | pyparsing==2.4.7 33 | pytest==6.1.2 34 | python-dateutil==2.8.1 35 | PyYAML==5.3.1 36 | regex==2020.11.13 37 | six==1.15.0 38 | SQLAlchemy==1.3.20 39 | text-unidecode==1.3 40 | toml==0.10.2 41 | typed-ast==1.4.1 42 | typing-extensions==3.7.4.3 43 | virtualenv==20.2.2 44 | Werkzeug==1.0.1 45 | wrapt==1.12.1 46 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from src.main.config import app 2 | 3 | if __name__ == "__main__": 4 | app.run(host="0.0.0.0", port=5000) 5 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programadorLhama/backend_project_python-lab-/95ab11b8be3edca3d34888708b41bd2acac3d3e7/src/__init__.py -------------------------------------------------------------------------------- /src/data/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_pet import FindPet 2 | from .find_user import FindUser 3 | from .register_pet import RegisterPet 4 | from .register_user import RegisterUser 5 | -------------------------------------------------------------------------------- /src/data/find_pet/__init__.py: -------------------------------------------------------------------------------- 1 | from .find import FindPet 2 | -------------------------------------------------------------------------------- /src/data/find_pet/find.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Type 2 | from src.data.interfaces import PetRepositoryInterface as PetRepository 3 | from src.domain.use_cases import FindPet as FindPetInterface 4 | from src.domain.models import Pets 5 | 6 | 7 | class FindPet(FindPetInterface): 8 | """ Class to define usecase: Find Pet """ 9 | 10 | def __init__(self, pet_repository: Type[PetRepository]): 11 | self.pet_repository = pet_repository 12 | 13 | def by_pet_id(self, pet_id: int) -> Dict[bool, List[Pets]]: 14 | """Select Pet By pet_id 15 | :param - pet_id: id of the pet 16 | :return - Dictionary with informations of the process 17 | """ 18 | 19 | response = None 20 | validate_entry = isinstance(pet_id, int) 21 | 22 | if validate_entry: 23 | response = self.pet_repository.select_pet(pet_id=pet_id) 24 | 25 | return {"Success": validate_entry, "Data": response} 26 | 27 | def by_user_id(self, user_id: int) -> Dict[bool, List[Pets]]: 28 | """Select Pet By user_id 29 | :param - user_id: id of the user owne of the pet 30 | :return - Dictionary with informations of the process 31 | """ 32 | 33 | response = None 34 | validate_entry = isinstance(user_id, int) 35 | 36 | if validate_entry: 37 | response = self.pet_repository.select_pet(user_id=user_id) 38 | 39 | return {"Success": validate_entry, "Data": response} 40 | 41 | def by_pet_id_and_user_id( 42 | self, pet_id: int, user_id: int 43 | ) -> Dict[bool, List[Pets]]: 44 | """Select Pet By user_id 45 | :param - user_id: id of the user owne of the pet 46 | :return - Dictionary with informations of the process 47 | """ 48 | 49 | response = None 50 | validate_entry = isinstance(user_id, int) and isinstance(pet_id, int) 51 | 52 | if validate_entry: 53 | response = self.pet_repository.select_pet(pet_id=pet_id, user_id=user_id) 54 | 55 | return {"Success": validate_entry, "Data": response} 56 | -------------------------------------------------------------------------------- /src/data/find_pet/find_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import PetRepositorySpy 3 | from .find import FindPet 4 | 5 | faker = Faker() 6 | 7 | 8 | def test_by_pet_id(): 9 | """ Testing pet_id method in FindPet """ 10 | 11 | pet_repo = PetRepositorySpy() 12 | find_pet = FindPet(pet_repo) 13 | 14 | attribute = {"pet_id": faker.random_number(digits=2)} 15 | response = find_pet.by_pet_id(pet_id=attribute["pet_id"]) 16 | 17 | # Testing Input 18 | assert pet_repo.select_pet_param["pet_id"] == attribute["pet_id"] 19 | 20 | # Testing Outputs 21 | assert response["Success"] is True 22 | assert response["Data"] 23 | 24 | 25 | def test_fail_by_pet_id(): 26 | """ Testing pet_id fail method in FindPet """ 27 | 28 | pet_repo = PetRepositorySpy() 29 | find_pet = FindPet(pet_repo) 30 | 31 | attribute = {"pet_id": faker.word()} 32 | response = find_pet.by_pet_id(pet_id=attribute["pet_id"]) 33 | 34 | # Testing Input 35 | assert pet_repo.select_pet_param == {} 36 | 37 | # Testing Outputs 38 | assert response["Success"] is False 39 | assert response["Data"] is None 40 | 41 | 42 | def test_by_user_id(): 43 | """ Testing by_id method in FindPet """ 44 | 45 | pet_repo = PetRepositorySpy() 46 | find_pet = FindPet(pet_repo) 47 | 48 | attribute = {"user_id": faker.random_number(digits=2)} 49 | response = find_pet.by_user_id(user_id=attribute["user_id"]) 50 | 51 | # Testing Input 52 | assert pet_repo.select_pet_param["user_id"] == attribute["user_id"] 53 | 54 | # Testing Outputs 55 | assert response["Success"] is True 56 | assert response["Data"] 57 | 58 | 59 | def test_fail_by_user_id(): 60 | """ Testing by_id fail method in FindPet """ 61 | 62 | pet_repo = PetRepositorySpy() 63 | find_pet = FindPet(pet_repo) 64 | 65 | attribute = {"user_id": faker.word()} 66 | response = find_pet.by_user_id(user_id=attribute["user_id"]) 67 | 68 | # Testing Input 69 | assert pet_repo.select_pet_param == {} 70 | 71 | # Testing Outputs 72 | assert response["Success"] is False 73 | assert response["Data"] is None 74 | 75 | 76 | def test_by_pet_id_and_user_id(): 77 | """ Testing by_pet_id_and_user_id method in FindPet """ 78 | 79 | pet_repo = PetRepositorySpy() 80 | find_pet = FindPet(pet_repo) 81 | 82 | attribute = { 83 | "user_id": faker.random_number(digits=2), 84 | "pet_id": faker.random_number(digits=2), 85 | } 86 | response = find_pet.by_pet_id_and_user_id( 87 | user_id=attribute["user_id"], pet_id=attribute["pet_id"] 88 | ) 89 | 90 | # Testing Input 91 | assert pet_repo.select_pet_param["user_id"] == attribute["user_id"] 92 | assert pet_repo.select_pet_param["pet_id"] == attribute["pet_id"] 93 | 94 | # Testing Outputs 95 | assert response["Success"] is True 96 | assert response["Data"] 97 | 98 | 99 | def test_fail_by_pet_id_and_user_id(): 100 | """ Testing by_pet_id_and_user_id fail method in FindPet """ 101 | 102 | pet_repo = PetRepositorySpy() 103 | find_pet = FindPet(pet_repo) 104 | 105 | attribute = {"user_id": faker.word(), "pet_id": faker.word()} 106 | response = find_pet.by_pet_id_and_user_id( 107 | user_id=attribute["user_id"], pet_id=attribute["pet_id"] 108 | ) 109 | 110 | # Testing Input 111 | assert pet_repo.select_pet_param == {} 112 | 113 | # Testing Outputs 114 | assert response["Success"] is False 115 | assert response["Data"] is None 116 | -------------------------------------------------------------------------------- /src/data/find_user/__init__.py: -------------------------------------------------------------------------------- 1 | from .find import FindUser 2 | -------------------------------------------------------------------------------- /src/data/find_user/find.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Type 2 | from src.data.interfaces import UserRepositoryInterface as UserRepository 3 | from src.domain.use_cases import FindUser as FindUserInterface 4 | from src.domain.models import Users 5 | 6 | 7 | class FindUser(FindUserInterface): 8 | """ Class to define usecase: Select User """ 9 | 10 | def __init__(self, user_repository: Type[UserRepository]): 11 | self.user_repository = user_repository 12 | 13 | def by_id(self, user_id: int) -> Dict[bool, List[Users]]: 14 | """Select User By id 15 | :param - user_id: id of the user 16 | :return - Dictionary with informations of the process 17 | """ 18 | 19 | response = None 20 | validate_entry = isinstance(user_id, int) 21 | 22 | if validate_entry: 23 | response = self.user_repository.select_user(user_id=user_id) 24 | 25 | return {"Success": validate_entry, "Data": response} 26 | 27 | def by_name(self, name: str) -> Dict[bool, List[Users]]: 28 | """Select User By name 29 | :param - name: name of the user 30 | :return - Dictionary with informations of the process 31 | """ 32 | 33 | response = None 34 | validate_entry = isinstance(name, str) 35 | 36 | if validate_entry: 37 | response = self.user_repository.select_user(name=name) 38 | 39 | return {"Success": validate_entry, "Data": response} 40 | 41 | def by_id_and_user(self, user_id: int, name: str) -> Dict[bool, List[Users]]: 42 | """Select User By id and name 43 | :param - name: name of the user 44 | :return - Dictionary with informations of the process 45 | """ 46 | 47 | response = None 48 | validate_entry = isinstance(user_id, int) and isinstance(name, str) 49 | 50 | if validate_entry: 51 | response = self.user_repository.select_user(user_id=user_id, name=name) 52 | 53 | return {"Success": validate_entry, "Data": response} 54 | -------------------------------------------------------------------------------- /src/data/find_user/find_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import UserRepositorySpy 3 | from .find import FindUser 4 | 5 | faker = Faker() 6 | 7 | 8 | def test_by_id(): 9 | """ Testing by_id method in FindUser """ 10 | 11 | user_repo = UserRepositorySpy() 12 | find_user = FindUser(user_repo) 13 | 14 | attribute = {"id": faker.random_number(digits=2)} 15 | response = find_user.by_id(user_id=attribute["id"]) 16 | 17 | # Testing Input 18 | assert user_repo.select_user_params["user_id"] == attribute["id"] 19 | 20 | # Testing Outputs 21 | assert response["Success"] is True 22 | assert response["Data"] 23 | 24 | 25 | def test_fail_by_id(): 26 | """ Testing by_id fail method in FindUser """ 27 | 28 | user_repo = UserRepositorySpy() 29 | find_user = FindUser(user_repo) 30 | 31 | attribute = {"id": faker.word()} 32 | response = find_user.by_id(user_id=attribute["id"]) 33 | 34 | # Testing Input 35 | assert user_repo.select_user_params == {} 36 | 37 | # Testing Outputs 38 | assert response["Success"] is False 39 | assert response["Data"] is None 40 | 41 | 42 | def test_by_name(): 43 | """ Testing by_name method in FindUser """ 44 | 45 | user_repo = UserRepositorySpy() 46 | find_user = FindUser(user_repo) 47 | 48 | attribute = {"name": faker.word()} 49 | response = find_user.by_name(name=attribute["name"]) 50 | 51 | # Testing Input 52 | assert user_repo.select_user_params["name"] == attribute["name"] 53 | 54 | # Testing Outputs 55 | assert response["Success"] is True 56 | assert response["Data"] 57 | 58 | 59 | def test_fail_by_name(): 60 | """ Testing by_name fail method in FindUser """ 61 | 62 | user_repo = UserRepositorySpy() 63 | find_user = FindUser(user_repo) 64 | 65 | attribute = {"name": faker.random_number(digits=2)} 66 | response = find_user.by_name(name=attribute["name"]) 67 | 68 | # Testing Input 69 | assert user_repo.select_user_params == {} 70 | 71 | # Testing Outputs 72 | assert response["Success"] is False 73 | assert response["Data"] is None 74 | 75 | 76 | def test_by_id_and_user(): 77 | """ Testing by_id_and_user method in FindUser """ 78 | 79 | user_repo = UserRepositorySpy() 80 | find_user = FindUser(user_repo) 81 | 82 | attribute = {"user_id": faker.random_number(digits=2), "name": faker.word()} 83 | 84 | response = find_user.by_id_and_user( 85 | user_id=attribute["user_id"], name=attribute["name"] 86 | ) 87 | 88 | # Testing Input 89 | assert user_repo.select_user_params["user_id"] == attribute["user_id"] 90 | assert user_repo.select_user_params["name"] == attribute["name"] 91 | 92 | # Testing Outputs 93 | assert response["Success"] is True 94 | assert response["Data"] 95 | 96 | 97 | def test_fail_by_id_and_user(): 98 | """ Testing by_id_and_user fail method in FindUser """ 99 | 100 | user_repo = UserRepositorySpy() 101 | find_user = FindUser(user_repo) 102 | 103 | attribute = { 104 | "user_id": faker.random_number(digits=2), 105 | "name": faker.random_number(digits=2), 106 | } 107 | 108 | response = find_user.by_id_and_user( 109 | user_id=attribute["user_id"], name=attribute["name"] 110 | ) 111 | 112 | # Testing Input 113 | assert user_repo.select_user_params == {} 114 | 115 | # Testing Outputs 116 | assert response["Success"] is False 117 | assert response["Data"] is None 118 | -------------------------------------------------------------------------------- /src/data/interfaces/__init__.py: -------------------------------------------------------------------------------- 1 | from .pet_repository_interface import PetRepositoryInterface 2 | from .user_repository_interface import UserRepositoryInterface 3 | -------------------------------------------------------------------------------- /src/data/interfaces/pet_repository_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | from src.domain.models import Pets 4 | 5 | 6 | class PetRepositoryInterface(ABC): 7 | """ Interface to Pet Repository """ 8 | 9 | @abstractmethod 10 | def insert_pet(self, name: str, specie: str, age: int, user_id: int) -> Pets: 11 | """ Insert data in pets entity """ 12 | 13 | raise Exception("Should implement method: insert_pet") 14 | 15 | @abstractmethod 16 | def select_pet(self, pet_id: int = None, user_id: int = None) -> List[Pets]: 17 | """ Select data in pets entity """ 18 | 19 | raise Exception("Should implement method: select_pet") 20 | -------------------------------------------------------------------------------- /src/data/interfaces/user_repository_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | from src.domain.models import Users 4 | 5 | 6 | class UserRepositoryInterface(ABC): 7 | """ Interface to User Repository """ 8 | 9 | @abstractmethod 10 | def insert_user(self, name: str, password: str) -> Users: 11 | """ Insert data in user entity """ 12 | 13 | raise Exception("Should implement method: insert_user") 14 | 15 | @abstractmethod 16 | def select_user(self, user_id: int = None, name: str = None) -> List[Users]: 17 | """ Select data in user entity """ 18 | 19 | raise Exception("Should implement method: select_pet") 20 | -------------------------------------------------------------------------------- /src/data/register_pet/__init__.py: -------------------------------------------------------------------------------- 1 | from .register import RegisterPet 2 | -------------------------------------------------------------------------------- /src/data/register_pet/register.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Type 2 | from src.data.find_user import FindUser 3 | from src.data.interfaces import PetRepositoryInterface as PetRepository 4 | from src.domain.use_cases import RegisterPet as RegistryPetInterface 5 | from src.domain.models import Users, Pets 6 | 7 | 8 | class RegisterPet(RegistryPetInterface): 9 | """ Class to define usecase: Register Pet """ 10 | 11 | def __init__(self, pet_repository: Type[PetRepository], find_user: Type[FindUser]): 12 | self.pet_repository = pet_repository 13 | self.find_user = find_user 14 | 15 | def registry( 16 | self, name: str, specie: str, user_information: Dict[int, str], age: int = None 17 | ) -> Dict[bool, Pets]: 18 | """Registry pet 19 | :param - name: pet name 20 | - specie: type of the specie 21 | - age: age of the pet 22 | - user_information: Dictionary with user_id and/or user_name 23 | :return - Dictionary with informations of the process 24 | """ 25 | 26 | response = None 27 | 28 | # Validating entry and trying to find an user 29 | validate_entry = isinstance(name, str) and isinstance(specie, str) 30 | user = self.__find_user_information(user_information) 31 | checker = validate_entry and user["Success"] 32 | 33 | if checker: 34 | response = self.pet_repository.insert_pet( 35 | name, specie, age, user_information["user_id"] 36 | ) 37 | 38 | return {"Success": checker, "Data": response} 39 | 40 | def __find_user_information( 41 | self, user_information: Dict[int, str] 42 | ) -> Dict[bool, List[Users]]: 43 | """Check userInfo Dicionaty and select user 44 | :param - user_information: Dictionary with user_id and/or user_name 45 | :return - Dictionary with the response of find_user use case 46 | """ 47 | 48 | user_founded = None 49 | user_params = user_information.keys() 50 | 51 | if "user_id" in user_params and "user_name" in user_params: 52 | # find user by id and name 53 | user_founded = self.find_user.by_id_and_user( 54 | user_information["user_id"], user_information["user_name"] 55 | ) 56 | 57 | elif "user_name" not in user_params and "user_id" in user_params: 58 | # find user by id 59 | user_founded = self.find_user.by_id(user_information["user_id"]) 60 | 61 | elif "user_id" not in user_params and "user_name" in user_params: 62 | # find user by name 63 | user_founded = self.find_user.by_name(user_information["user_name"]) 64 | 65 | else: 66 | return {"Success": False, "Data": None} 67 | 68 | return user_founded 69 | -------------------------------------------------------------------------------- /src/data/register_pet/register_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import PetRepositorySpy, UserRepositorySpy 3 | from src.data.test import FindUserSpy 4 | from .register import RegisterPet 5 | 6 | faker = Faker() 7 | 8 | 9 | def test_registry(): 10 | """ Testing registry method in RegisterPet """ 11 | 12 | pet_repo = PetRepositorySpy() 13 | find_user = FindUserSpy(UserRepositorySpy()) 14 | registry_pet = RegisterPet(pet_repo, find_user) 15 | 16 | attributes = { 17 | "name": faker.name(), 18 | "specie": faker.name(), 19 | "age": faker.random_number(digits=1), 20 | "user_information": { 21 | "user_id": faker.random_number(digits=1), 22 | "user_name": faker.name(), 23 | }, 24 | } 25 | 26 | response = registry_pet.registry( 27 | name=attributes["name"], 28 | specie=attributes["specie"], 29 | age=attributes["age"], 30 | user_information=attributes["user_information"], 31 | ) 32 | 33 | # Testing Inputs 34 | assert pet_repo.insert_pet_param["name"] == attributes["name"] 35 | assert pet_repo.insert_pet_param["specie"] == attributes["specie"] 36 | assert pet_repo.insert_pet_param["age"] == attributes["age"] 37 | assert ( 38 | pet_repo.insert_pet_param["user_id"] 39 | == attributes["user_information"]["user_id"] 40 | ) 41 | 42 | # Testing FindUser Inputs 43 | assert ( 44 | find_user.by_id_and_user_param["user_id"] 45 | == attributes["user_information"]["user_id"] 46 | ) 47 | assert ( 48 | find_user.by_id_and_user_param["name"] 49 | == attributes["user_information"]["user_name"] 50 | ) 51 | 52 | # Testing Outputs 53 | assert response["Success"] is True 54 | assert response["Data"] 55 | 56 | 57 | def test_registry_fail_attributes(): 58 | """ Testing registry fail method in RegisterPet by attributes """ 59 | 60 | pet_repo = PetRepositorySpy() 61 | find_user = FindUserSpy(UserRepositorySpy()) 62 | registry_pet = RegisterPet(pet_repo, find_user) 63 | 64 | attributes = { 65 | "name": faker.name(), 66 | "specie": faker.random_number(), 67 | "age": faker.name(), 68 | "user_information": { 69 | "user_id": faker.random_number(digits=1), 70 | "user_name": faker.name(), 71 | }, 72 | } 73 | 74 | response = registry_pet.registry( 75 | name=attributes["name"], 76 | specie=attributes["specie"], 77 | age=attributes["age"], 78 | user_information=attributes["user_information"], 79 | ) 80 | 81 | # Testing Inputs 82 | assert pet_repo.insert_pet_param == {} 83 | 84 | # Testing Outputs 85 | assert response["Success"] is False 86 | assert response["Data"] is None 87 | 88 | 89 | def test_registry_fail_user(): 90 | """ Testing registry fail method in RegisterPet by user search """ 91 | 92 | pet_repo = PetRepositorySpy() 93 | find_user = FindUserSpy(UserRepositorySpy()) 94 | registry_pet = RegisterPet(pet_repo, find_user) 95 | 96 | attributes = { 97 | "name": faker.name(), 98 | "specie": faker.name(), 99 | "age": faker.random_number(digits=1), 100 | "user_information": { 101 | "user_id": faker.random_number(digits=1), 102 | "user_name": faker.random_number(), 103 | }, 104 | } 105 | 106 | response = registry_pet.registry( 107 | name=attributes["name"], 108 | specie=attributes["specie"], 109 | age=attributes["age"], 110 | user_information=attributes["user_information"], 111 | ) 112 | 113 | # Testing Inputs 114 | assert pet_repo.insert_pet_param == {} 115 | 116 | # Testing FindUser Inputs 117 | assert ( 118 | find_user.by_id_and_user_param["user_id"] 119 | == attributes["user_information"]["user_id"] 120 | ) 121 | assert ( 122 | find_user.by_id_and_user_param["name"] 123 | == attributes["user_information"]["user_name"] 124 | ) 125 | 126 | # Testing Outputs 127 | assert response["Success"] is False 128 | assert response["Data"] is None 129 | -------------------------------------------------------------------------------- /src/data/register_user/__init__.py: -------------------------------------------------------------------------------- 1 | from .register import RegisterUser 2 | -------------------------------------------------------------------------------- /src/data/register_user/register.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Type 2 | from src.data.interfaces import UserRepositoryInterface as UserRepository 3 | from src.domain.use_cases import RegisterUser as RegisterUserInterface 4 | from src.domain.models import Users 5 | 6 | 7 | class RegisterUser(RegisterUserInterface): 8 | """ Class to define usecase: Register User """ 9 | 10 | def __init__(self, user_repository: Type[UserRepository]): 11 | self.user_repository = user_repository 12 | 13 | def registry(self, name: str, password: str) -> Dict[bool, Users]: 14 | """Registry user 15 | :param - name: person name 16 | - password: password of the person 17 | :return - Dictionary with informations of the process 18 | """ 19 | 20 | response = None 21 | validate_entry = isinstance(name, str) and isinstance(password, str) 22 | 23 | if validate_entry: 24 | response = self.user_repository.insert_user(name, password) 25 | 26 | return {"Success": validate_entry, "Data": response} 27 | -------------------------------------------------------------------------------- /src/data/register_user/register_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import UserRepositorySpy 3 | from .register import RegisterUser 4 | 5 | faker = Faker() 6 | 7 | 8 | def test_registry(): 9 | """ Testing registry method in RegisterUser """ 10 | 11 | user_repo = UserRepositorySpy() 12 | registry_user = RegisterUser(user_repo) 13 | 14 | attributes = { 15 | "name": faker.name(), 16 | "password": faker.word(), 17 | } 18 | 19 | response = registry_user.registry( 20 | name=attributes["name"], password=attributes["password"] 21 | ) 22 | 23 | # Testing Inputs 24 | assert user_repo.insert_user_params["name"] == attributes["name"] 25 | assert user_repo.insert_user_params["password"] == attributes["password"] 26 | 27 | # Testing Outputs 28 | assert response["Success"] is True 29 | assert response["Data"] 30 | 31 | 32 | def test_registry_fail(): 33 | """ Testing registry fail method in RegisterUser """ 34 | 35 | user_repo = UserRepositorySpy() 36 | registry_user = RegisterUser(user_repo) 37 | 38 | attributes = { 39 | "name": faker.random_number(digits=3), 40 | "password": faker.word(), 41 | } 42 | 43 | response = registry_user.registry( 44 | name=attributes["name"], password=attributes["password"] 45 | ) 46 | 47 | # Testing Inputs 48 | assert user_repo.insert_user_params == {} 49 | 50 | # Testing Outputs 51 | assert response["Success"] is False 52 | assert response["Data"] is None 53 | -------------------------------------------------------------------------------- /src/data/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_user_spy import FindUserSpy 2 | from .find_pet_spy import FindPetSpy 3 | from .register_pet_spy import RegisterPetSpy 4 | from .register_user_spy import RegisterUserSpy 5 | -------------------------------------------------------------------------------- /src/data/test/find_pet_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | from src.domain.models import Pets 3 | from src.domain.test import mock_pet 4 | 5 | 6 | class FindPetSpy: 7 | """ Class to mock usecase: Find Pet """ 8 | 9 | def __init__(self, pet_repository: any): 10 | self.pet_repository = pet_repository 11 | self.by_pet_id_param = {} 12 | self.by_user_id_param = {} 13 | self.by_pet_id_and_user_id_param = {} 14 | 15 | def by_pet_id(self, pet_id: int) -> Dict[bool, List[Pets]]: 16 | """ Select Pet By pet_id """ 17 | 18 | self.by_pet_id_param["pet_id"] = pet_id 19 | response = None 20 | validate_entry = isinstance(pet_id, int) 21 | 22 | if validate_entry: 23 | response = [mock_pet()] 24 | 25 | return {"Success": validate_entry, "Data": response} 26 | 27 | def by_user_id(self, user_id: int) -> Dict[bool, List[Pets]]: 28 | """ Select Pet By user_id """ 29 | 30 | self.by_user_id_param["user_id"] = user_id 31 | response = None 32 | validate_entry = isinstance(user_id, int) 33 | 34 | if validate_entry: 35 | response = [mock_pet()] 36 | 37 | return {"Success": validate_entry, "Data": response} 38 | 39 | def by_pet_id_and_user_id( 40 | self, pet_id: int, user_id: int 41 | ) -> Dict[bool, List[Pets]]: 42 | """ Select Pet By user_id """ 43 | 44 | self.by_pet_id_and_user_id_param["pet_id"] = pet_id 45 | self.by_pet_id_and_user_id_param["user_id"] = user_id 46 | response = None 47 | validate_entry = isinstance(user_id, int) and isinstance(pet_id, int) 48 | 49 | if validate_entry: 50 | response = [mock_pet()] 51 | 52 | return {"Success": validate_entry, "Data": response} 53 | -------------------------------------------------------------------------------- /src/data/test/find_user_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | from src.domain.models import Users 3 | from src.domain.test import mock_users 4 | 5 | 6 | class FindUserSpy: 7 | """ Class to define usecase: Select User """ 8 | 9 | def __init__(self, user_repository: any): 10 | self.user_repository = user_repository 11 | self.by_id_param = {} 12 | self.by_name_param = {} 13 | self.by_id_and_user_param = {} 14 | 15 | def by_id(self, user_id: int) -> Dict[bool, List[Users]]: 16 | """ Select User By id """ 17 | 18 | self.by_id_param["user_id"] = user_id 19 | response = None 20 | validate_entry = isinstance(user_id, int) 21 | 22 | if validate_entry: 23 | response = [mock_users()] 24 | 25 | return {"Success": validate_entry, "Data": response} 26 | 27 | def by_name(self, name: str) -> Dict[bool, List[Users]]: 28 | """ Select User By name """ 29 | 30 | self.by_name_param["name"] = name 31 | response = None 32 | validate_entry = isinstance(name, str) 33 | 34 | if validate_entry: 35 | response = [mock_users()] 36 | 37 | return {"Success": validate_entry, "Data": response} 38 | 39 | def by_id_and_user(self, user_id: int, name: str) -> Dict[bool, List[Users]]: 40 | """ Select User By id and name """ 41 | 42 | self.by_id_and_user_param["user_id"] = user_id 43 | self.by_id_and_user_param["name"] = name 44 | response = None 45 | validate_entry = isinstance(user_id, int) and isinstance(name, str) 46 | 47 | if validate_entry: 48 | response = [mock_users()] 49 | 50 | return {"Success": validate_entry, "Data": response} 51 | -------------------------------------------------------------------------------- /src/data/test/register_pet_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | from src.domain.models import Users, Pets 3 | from src.domain.test import mock_pet, mock_users 4 | 5 | 6 | class RegisterPetSpy: 7 | """ Class to define usecase: Register Pet """ 8 | 9 | def __init__(self, pet_repository: any, find_user: any): 10 | self.pet_repository = pet_repository 11 | self.find_user = find_user 12 | self.registry_param = {} 13 | 14 | def registry( 15 | self, name: str, specie: str, user_information: Dict[int, str], age: int = None 16 | ) -> Dict[bool, Pets]: 17 | """ Registry pet """ 18 | 19 | self.registry_param["name"] = name 20 | self.registry_param["specie"] = specie 21 | self.registry_param["age"] = age 22 | self.registry_param["user_information"] = user_information 23 | 24 | response = None 25 | 26 | # Validating entry and trying to find an user 27 | validate_entry = isinstance(name, str) and isinstance(specie, str) 28 | user = self.__find_user_information(user_information) 29 | checker = validate_entry and user["Success"] 30 | 31 | if checker: 32 | response = mock_pet() 33 | 34 | return {"Success": checker, "Data": response} 35 | 36 | @classmethod 37 | def __find_user_information( 38 | cls, user_information: Dict[int, str] 39 | ) -> Dict[bool, List[Users]]: 40 | """ Check userInfo Dicionaty and select user """ 41 | 42 | user_founded = None 43 | user_params = user_information.keys() 44 | 45 | if "user_id" and "user_name" in user_params: 46 | # find user by id and name 47 | user_founded = {"Success": True, "Data": mock_users()} 48 | 49 | elif "user_name" not in user_params and "user_id" in user_params: 50 | # find user by id 51 | user_founded = {"Success": True, "Data": mock_users()} 52 | 53 | elif "user_id" not in user_params and "user_name" in user_params: 54 | # find user by name 55 | user_founded = {"Success": True, "Data": mock_users()} 56 | 57 | else: 58 | return {"Success": False, "Data": None} 59 | 60 | return user_founded 61 | -------------------------------------------------------------------------------- /src/data/test/register_user_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from src.domain.models import Users 3 | from src.domain.test import mock_users 4 | 5 | 6 | class RegisterUserSpy: 7 | """ Class to define usecase: Register User """ 8 | 9 | def __init__(self, user_repository: any): 10 | self.user_repository = user_repository 11 | self.registry_param = {} 12 | 13 | def registry(self, name: str, password: str) -> Dict[bool, Users]: 14 | """ Registry user """ 15 | 16 | self.registry_param["name"] = name 17 | self.registry_param["password"] = password 18 | 19 | response = None 20 | validate_entry = isinstance(name, str) and isinstance(password, str) 21 | 22 | if validate_entry: 23 | response = mock_users() 24 | 25 | return {"Success": validate_entry, "Data": response} 26 | -------------------------------------------------------------------------------- /src/domain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programadorLhama/backend_project_python-lab-/95ab11b8be3edca3d34888708b41bd2acac3d3e7/src/domain/__init__.py -------------------------------------------------------------------------------- /src/domain/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .users import Users 2 | from .pets import Pets 3 | -------------------------------------------------------------------------------- /src/domain/models/pets.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Pets = namedtuple("Pets", "id name specie age user_id") 4 | -------------------------------------------------------------------------------- /src/domain/models/users.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Users = namedtuple("Users", "id name password") 4 | -------------------------------------------------------------------------------- /src/domain/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .mock_pet import mock_pet 2 | from .mock_user import mock_users 3 | -------------------------------------------------------------------------------- /src/domain/test/mock_pet.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.domain.models import Pets 3 | 4 | faker = Faker() 5 | 6 | 7 | def mock_pet() -> Pets: 8 | """Mocking Pet 9 | :param - None 10 | :return - Fake Pet registry 11 | """ 12 | 13 | return Pets( 14 | id=faker.random_number(digits=5), 15 | name=faker.name(), 16 | specie="dog", 17 | age=faker.random_number(digits=1), 18 | user_id=faker.random_number(digits=5), 19 | ) 20 | -------------------------------------------------------------------------------- /src/domain/test/mock_user.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.domain.models import Users 3 | 4 | faker = Faker() 5 | 6 | 7 | def mock_users() -> Users: 8 | """Mocking Users 9 | :param - None 10 | :param Fake User registry 11 | """ 12 | 13 | return Users( 14 | id=faker.random_number(digits=5), name=faker.name(), password=faker.name() 15 | ) 16 | -------------------------------------------------------------------------------- /src/domain/use_cases/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_pet import FindPet 2 | from .find_user import FindUser 3 | from .register_pet import RegisterPet 4 | from .register_user import RegisterUser 5 | -------------------------------------------------------------------------------- /src/domain/use_cases/find_pet.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict, List 3 | from src.domain.models import Pets 4 | 5 | 6 | class FindPet(ABC): 7 | """ Interface to FindPet use case """ 8 | 9 | @abstractmethod 10 | def by_pet_id(self, pet_id: int) -> Dict[bool, List[Pets]]: 11 | """ Specific case """ 12 | 13 | raise Exception("Should implement method: by_pet_id") 14 | 15 | @abstractmethod 16 | def by_user_id(self, user_id: int) -> Dict[bool, List[Pets]]: 17 | """ Specific case """ 18 | 19 | raise Exception("Should implement method: by_user_id") 20 | 21 | @abstractmethod 22 | def by_pet_id_and_user_id( 23 | self, pet_id: int, user_id: int 24 | ) -> Dict[bool, List[Pets]]: 25 | """ Specific case """ 26 | 27 | raise Exception("Should implement method: by_pet_id_and_user_id") 28 | -------------------------------------------------------------------------------- /src/domain/use_cases/find_user.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict, List 3 | from src.domain.models import Users 4 | 5 | 6 | class FindUser(ABC): 7 | """ Interface to FindPet use case """ 8 | 9 | @abstractmethod 10 | def by_id(self, user_id: int) -> Dict[bool, List[Users]]: 11 | """ Specific case """ 12 | 13 | raise Exception("Should implement method: by_id") 14 | 15 | @abstractmethod 16 | def by_name(self, name: str) -> Dict[bool, List[Users]]: 17 | """ Specific case """ 18 | 19 | raise Exception("Should implement method: by_name") 20 | 21 | @abstractmethod 22 | def by_id_and_user(self, user_id: int, name: str) -> Dict[bool, List[Users]]: 23 | """ Specific case """ 24 | 25 | raise Exception("Should implement method: by_id_and_user") 26 | -------------------------------------------------------------------------------- /src/domain/use_cases/register_pet.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict 3 | from src.domain.models import Pets 4 | 5 | 6 | class RegisterPet(ABC): 7 | """ Interface to FindPet use case """ 8 | 9 | @abstractmethod 10 | def registry( 11 | self, name: str, specie: str, user_information: Dict[int, str], age: int = None 12 | ) -> Dict[bool, Pets]: 13 | """ Case """ 14 | 15 | raise Exception("Should implement method: registry") 16 | -------------------------------------------------------------------------------- /src/domain/use_cases/register_user.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict 3 | from src.domain.models import Users 4 | 5 | 6 | class RegisterUser(ABC): 7 | """ Interface to FindPet use case """ 8 | 9 | @abstractmethod 10 | def registry(self, name: str, password: str) -> Dict[bool, Users]: 11 | """ Case """ 12 | 13 | raise Exception("Should implement method: registry") 14 | -------------------------------------------------------------------------------- /src/infra/__init__.py: -------------------------------------------------------------------------------- 1 | from .repositorys import UserRepository, PetRepository 2 | -------------------------------------------------------------------------------- /src/infra/configs/__init__.py: -------------------------------------------------------------------------------- 1 | from .db_base import Base 2 | from .db_config import DBConnectionHandler 3 | -------------------------------------------------------------------------------- /src/infra/configs/db_base.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | 3 | 4 | Base = declarative_base() 5 | -------------------------------------------------------------------------------- /src/infra/configs/db_config.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import sessionmaker 3 | 4 | 5 | class DBConnectionHandler: 6 | """SQLAlchemy database connection""" 7 | 8 | def __init__(self): 9 | self.__connection_string = "sqlite:///storage.db" 10 | self.session = None 11 | 12 | def get_engine(self): 13 | """Return connection Engine 14 | :param - None 15 | :return - engine connection to database 16 | """ 17 | 18 | engine = create_engine(self.__connection_string) 19 | return engine 20 | 21 | def __enter__(self): 22 | engine = create_engine(self.__connection_string) 23 | session_maker = sessionmaker() 24 | self.session = session_maker(bind=engine) 25 | return self 26 | 27 | def __exit__(self, exc_type, exc_val, exc_tb): 28 | self.session.close() # pylint: disable=no-member 29 | -------------------------------------------------------------------------------- /src/infra/entities/__init__.py: -------------------------------------------------------------------------------- 1 | from .users import Users 2 | from .pets import Pets 3 | -------------------------------------------------------------------------------- /src/infra/entities/pets.py: -------------------------------------------------------------------------------- 1 | import enum 2 | from sqlalchemy import Column, Integer, String, Enum, ForeignKey 3 | from src.infra.configs import Base 4 | 5 | 6 | class AnimalTypes(enum.Enum): 7 | """ Defining Animals Types """ 8 | 9 | dog = "dog" 10 | cat = "cat" 11 | fish = "fish" 12 | turtle = "turtle" 13 | 14 | 15 | class Pets(Base): 16 | """ Pets Entity """ 17 | 18 | __tablename__ = "pets" 19 | 20 | id = Column(Integer, primary_key=True) 21 | name = Column(String(20), nullable=False, unique=True) 22 | specie = Column(Enum(AnimalTypes), nullable=False) 23 | age = Column(Integer) 24 | user_id = Column(Integer, ForeignKey("users.id")) 25 | 26 | def __repr__(self): 27 | return f"Pet: [name={self.name}, specie={self.specie}, age={self.age}]" 28 | 29 | def __eq__(self, other): 30 | if ( 31 | self.id == other.id 32 | and self.name == other.name 33 | and self.specie == other.specie 34 | and self.age == other.age 35 | and self.user_id == other.user_id 36 | ): 37 | return True 38 | 39 | return False 40 | -------------------------------------------------------------------------------- /src/infra/entities/users.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from sqlalchemy.orm import relationship 3 | from src.infra.configs import Base 4 | 5 | 6 | class Users(Base): 7 | """ Users Entity """ 8 | 9 | __tablename__ = "users" 10 | 11 | id = Column(Integer, primary_key=True) 12 | name = Column(String, nullable=False, unique=True) 13 | password = Column(String, nullable=False) 14 | id_pet = relationship("Pets") 15 | 16 | def __repr__(self): 17 | return f"User [name={self.name}]" 18 | 19 | def __eq__(self, other): 20 | if ( 21 | self.id == other.id 22 | and self.name == other.name 23 | and self.password == other.password 24 | ): 25 | return True 26 | return False 27 | -------------------------------------------------------------------------------- /src/infra/repositorys/__init__.py: -------------------------------------------------------------------------------- 1 | from .user_repository import UserRepository 2 | from .pet_repository import PetRepository 3 | -------------------------------------------------------------------------------- /src/infra/repositorys/pet_repository.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=E1101 2 | 3 | from typing import List 4 | from sqlalchemy.orm.exc import NoResultFound 5 | from src.infra.entities import Pets as PetsEntity 6 | from src.infra.configs import DBConnectionHandler 7 | from src.domain.models import Pets 8 | from src.data.interfaces import PetRepositoryInterface 9 | 10 | 11 | class PetRepository(PetRepositoryInterface): 12 | """ Class to manage Pet Repository """ 13 | 14 | @classmethod 15 | def insert_pet(cls, name: str, specie: str, age: int, user_id: int) -> Pets: 16 | """ 17 | Insert data in PetsEntity entity 18 | :param - name: person name 19 | - specie: Enum with species acepted 20 | - age: age of the pet 21 | - user_id: id of the owner (FK) 22 | :return - tuple with new pet inserted informations 23 | """ 24 | 25 | with DBConnectionHandler() as db_connection: 26 | try: 27 | new_pet = PetsEntity(name=name, specie=specie, age=age, user_id=user_id) 28 | db_connection.session.add(new_pet) 29 | db_connection.session.commit() 30 | 31 | return Pets( 32 | id=new_pet.id, 33 | name=new_pet.name, 34 | specie=new_pet.specie.value, 35 | age=new_pet.age, 36 | user_id=new_pet.user_id, 37 | ) 38 | 39 | except: 40 | db_connection.session.rollback() 41 | raise 42 | finally: 43 | db_connection.session.close() 44 | 45 | return None 46 | 47 | @classmethod 48 | def select_pet(cls, pet_id: int = None, user_id: int = None) -> List[Pets]: 49 | """ 50 | Select data in PetsEntity entity by id and/or user_id 51 | :param - id: Id of the registry 52 | - name: User name in database 53 | :return - List with PetsEntity selected 54 | """ 55 | 56 | try: 57 | query_data = None 58 | 59 | if pet_id and not user_id: 60 | # Select pet by id 61 | with DBConnectionHandler() as db_connection: 62 | data = ( 63 | db_connection.session.query(PetsEntity) 64 | .filter_by(id=pet_id) 65 | .one() 66 | ) 67 | query_data = [data] 68 | 69 | elif not pet_id and user_id: 70 | # Select pet by user_id 71 | with DBConnectionHandler() as db_connection: 72 | data = ( 73 | db_connection.session.query(PetsEntity) 74 | .filter_by(user_id=user_id) 75 | .all() 76 | ) 77 | query_data = data 78 | 79 | elif pet_id and user_id: 80 | # Select pet by pet_id and user_id: 81 | with DBConnectionHandler() as db_connection: 82 | data = ( 83 | db_connection.session.query(PetsEntity) 84 | .filter_by(id=pet_id, user_id=user_id) 85 | .one() 86 | ) 87 | query_data = [data] 88 | 89 | return query_data 90 | 91 | except NoResultFound: 92 | return [] 93 | except: 94 | db_connection.session.rollback() 95 | raise 96 | finally: 97 | db_connection.session.close() 98 | 99 | return None 100 | -------------------------------------------------------------------------------- /src/infra/repositorys/pet_repository_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.entities import Pets 3 | from src.infra.entities.pets import AnimalTypes 4 | from src.infra.configs import DBConnectionHandler 5 | from .pet_repository import PetRepository 6 | 7 | faker = Faker() 8 | db_connection_handler = DBConnectionHandler() 9 | pet_repository = PetRepository() 10 | 11 | 12 | def test_insert_pet(): 13 | """ Should insert pet in Pet table and return it """ 14 | 15 | name = faker.name() 16 | specie = "fish" 17 | age = faker.random_number(digits=1) 18 | user_id = faker.random_number() 19 | 20 | # SQL commands 21 | new_pet = pet_repository.insert_pet(name, specie, age, user_id) 22 | engine = db_connection_handler.get_engine() 23 | query_user = engine.execute( 24 | "SELECT * FROM pets WHERE id='{}';".format(new_pet.id) 25 | ).fetchone() 26 | 27 | assert new_pet.id == query_user.id 28 | assert new_pet.name == query_user.name 29 | assert new_pet.specie == query_user.specie 30 | assert new_pet.age == query_user.age 31 | assert new_pet.user_id == query_user.user_id 32 | 33 | engine.execute("DELETE FROM pets WHERE id='{}';".format(new_pet.id)) 34 | 35 | 36 | def test_select_pet(): 37 | """ Should select a pet in Pets table compare it """ 38 | 39 | pet_id = faker.random_number(digits=4) 40 | name = faker.name() 41 | specie = "fish" 42 | age = faker.random_number(digits=1) 43 | user_id = faker.random_number() 44 | 45 | # SQL commands 46 | specie_mock = AnimalTypes("fish") 47 | data = Pets(id=pet_id, name=name, specie=specie_mock, age=age, user_id=user_id) 48 | 49 | engine = db_connection_handler.get_engine() 50 | engine.execute( 51 | "INSERT INTO pets (id, name, specie, age, user_id) VALUES ('{}','{}', '{}', '{}', '{}');".format( 52 | pet_id, name, specie, age, user_id 53 | ) 54 | ) 55 | query_pets1 = pet_repository.select_pet(pet_id=pet_id) 56 | query_pets2 = pet_repository.select_pet(user_id=user_id) 57 | query_pets3 = pet_repository.select_pet(pet_id=pet_id, user_id=user_id) 58 | 59 | assert data in query_pets1 60 | assert data in query_pets2 61 | assert data in query_pets3 62 | 63 | engine.execute("DELETE FROM pets WHERE id='{}';".format(data.id)) 64 | -------------------------------------------------------------------------------- /src/infra/repositorys/user_repository.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=E1101 2 | 3 | from typing import List 4 | from sqlalchemy.orm.exc import NoResultFound 5 | from src.domain.models import Users 6 | from src.infra.entities import Users as UsersModel 7 | from src.infra.configs import DBConnectionHandler 8 | from src.data.interfaces import UserRepositoryInterface 9 | 10 | 11 | class UserRepository(UserRepositoryInterface): 12 | """ Class to manage User Repository """ 13 | 14 | @classmethod 15 | def insert_user(cls, name: str, password: str) -> Users: 16 | """ 17 | Insert data in user entity 18 | :param - name: person name 19 | - password: user password 20 | :return - tuple with new user inserted informations 21 | """ 22 | 23 | # Creating a Return Tuple With Informations 24 | 25 | with DBConnectionHandler() as db_connection: 26 | try: 27 | new_user = UsersModel(name=name, password=password) 28 | db_connection.session.add(new_user) 29 | db_connection.session.commit() 30 | 31 | return Users( 32 | id=new_user.id, name=new_user.name, password=new_user.password 33 | ) 34 | 35 | except Exception as ex: 36 | db_connection.session.rollback() 37 | print(ex) 38 | raise 39 | finally: 40 | db_connection.session.close() 41 | 42 | return None 43 | 44 | @classmethod 45 | def select_user(cls, user_id: int = None, name: str = None) -> List[Users]: 46 | """ 47 | Select data in user entity by id and/or name 48 | :param - id: Id of the registry 49 | - name: User name in database 50 | :return - List with UsersModel selected 51 | """ 52 | 53 | try: 54 | query_data = None 55 | 56 | if user_id and not name: 57 | # Select user by id 58 | with DBConnectionHandler() as db_connection: 59 | data = ( 60 | db_connection.session.query(UsersModel) 61 | .filter_by(id=user_id) 62 | .one() 63 | ) 64 | query_data = [data] 65 | 66 | elif not user_id and name: 67 | # Select user by name 68 | with DBConnectionHandler() as db_connection: 69 | data = ( 70 | db_connection.session.query(UsersModel) 71 | .filter_by(name=name) 72 | .all() 73 | ) 74 | query_data = data 75 | 76 | elif user_id and name: 77 | # Select user by id and name 78 | with DBConnectionHandler() as db_connection: 79 | data = ( 80 | db_connection.session.query(UsersModel) 81 | .filter_by(id=user_id, name=name) 82 | .one() 83 | ) 84 | query_data = [data] 85 | 86 | return query_data 87 | 88 | except NoResultFound: 89 | return [] 90 | except Exception as ex: 91 | db_connection.session.rollback() 92 | print(ex) 93 | raise 94 | finally: 95 | db_connection.session.close() 96 | 97 | return None 98 | -------------------------------------------------------------------------------- /src/infra/repositorys/user_repository_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.entities import Users 3 | from src.infra.configs import DBConnectionHandler 4 | from .user_repository import UserRepository 5 | 6 | faker = Faker() 7 | db_connection_handler = DBConnectionHandler() 8 | user_repository = UserRepository() 9 | 10 | 11 | def test_insert_user(): 12 | """ Should insert user in Users table and return it """ 13 | 14 | name = faker.name() 15 | password = faker.word() 16 | engine = db_connection_handler.get_engine() 17 | 18 | # SQL commands 19 | new_user = user_repository.insert_user(name, password) 20 | query_user = engine.execute( 21 | "SELECT * FROM users WHERE id='{}';".format(new_user.id) 22 | ).fetchone() 23 | 24 | assert new_user.id == query_user.id 25 | assert new_user.name == query_user.name 26 | assert new_user.password == query_user.password 27 | 28 | engine.execute("DELETE FROM users WHERE id='{}';".format(new_user.id)) 29 | 30 | 31 | def test_select_user(): 32 | """ Should select a user in Users table compare it """ 33 | 34 | user_id = faker.random_number(digits=5) 35 | name = faker.name() 36 | password = faker.word() 37 | data = Users(id=user_id, name=name, password=password) 38 | 39 | # SQL commands 40 | engine = db_connection_handler.get_engine() 41 | engine.execute( 42 | "INSERT INTO users (id, name, password) VALUES ('{}','{}', '{}');".format( 43 | user_id, name, password 44 | ) 45 | ) 46 | query_users1 = user_repository.select_user(user_id=user_id) 47 | query_users2 = user_repository.select_user(name=name) 48 | query_users3 = user_repository.select_user(user_id=user_id, name=name) 49 | 50 | assert data in query_users1 51 | assert data in query_users2 52 | assert data in query_users3 53 | 54 | engine.execute("DELETE FROM users WHERE id='{}';".format(user_id)) 55 | -------------------------------------------------------------------------------- /src/infra/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .pet_repository_spy import PetRepositorySpy 2 | from .user_repository_spy import UserRepositorySpy 3 | -------------------------------------------------------------------------------- /src/infra/test/pet_repository_spy.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from src.domain.models import Pets 3 | from src.domain.test import mock_pet 4 | 5 | 6 | class PetRepositorySpy: 7 | """ Spy to Pet Repository """ 8 | 9 | def __init__(self): 10 | self.insert_pet_param = {} 11 | self.select_pet_param = {} 12 | 13 | def insert_pet(self, name: str, specie: str, age: int, user_id: int) -> Pets: 14 | """ Spy all the attributes """ 15 | 16 | self.insert_pet_param["name"] = name 17 | self.insert_pet_param["specie"] = specie 18 | self.insert_pet_param["age"] = age 19 | self.insert_pet_param["user_id"] = user_id 20 | 21 | return mock_pet() 22 | 23 | def select_pet(self, pet_id: int = None, user_id: int = None) -> List[Pets]: 24 | """ Spy all the attributes """ 25 | 26 | self.select_pet_param["pet_id"] = pet_id 27 | self.select_pet_param["user_id"] = user_id 28 | 29 | return [mock_pet()] 30 | -------------------------------------------------------------------------------- /src/infra/test/user_repository_spy.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from src.domain.models import Users 3 | from src.domain.test import mock_users 4 | 5 | 6 | class UserRepositorySpy: 7 | """ Spy to User Repository """ 8 | 9 | def __init__(self): 10 | self.insert_user_params = {} 11 | self.select_user_params = {} 12 | 13 | def insert_user(self, name: str, password: str) -> Users: 14 | """ Spy all the attributes """ 15 | 16 | self.insert_user_params["name"] = name 17 | self.insert_user_params["password"] = password 18 | 19 | return mock_users() 20 | 21 | def select_user(self, user_id: int = None, name: str = None) -> List[Users]: 22 | """ Spy all the attributes """ 23 | 24 | self.select_user_params["user_id"] = user_id 25 | self.select_user_params["name"] = name 26 | 27 | return [mock_users()] 28 | -------------------------------------------------------------------------------- /src/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programadorLhama/backend_project_python-lab-/95ab11b8be3edca3d34888708b41bd2acac3d3e7/src/main/__init__.py -------------------------------------------------------------------------------- /src/main/adapter/__init__.py: -------------------------------------------------------------------------------- 1 | from .router_adapter import flask_adapter 2 | -------------------------------------------------------------------------------- /src/main/adapter/router_adapter.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from sqlalchemy.exc import IntegrityError 3 | from src.presentation.helpers import HttpRequest, HttpResponse 4 | from src.presentation.errors import HttpErrors 5 | from src.main.interface import RouteInterface as Route 6 | 7 | 8 | def flask_adapter(request: any, api_route: Type[Route]) -> any: 9 | """Adapter pattern to Flask 10 | :param - Flask Request 11 | :api_route: Composite Routes 12 | """ 13 | 14 | try: 15 | # Query string params 16 | query_string_params = request.args.to_dict() 17 | 18 | # Formating information 19 | if "user_id" in query_string_params.keys(): 20 | query_string_params["user_id"] = int(query_string_params["user_id"]) 21 | 22 | if "pet_id" in query_string_params.keys(): 23 | query_string_params["pet_id"] = int(query_string_params["pet_id"]) 24 | except: 25 | https_error = HttpErrors.error_400() 26 | return HttpResponse( 27 | status_code=https_error["status_code"], body=https_error["body"] 28 | ) 29 | 30 | http_request = HttpRequest( 31 | header=request.headers, body=request.json, query=query_string_params 32 | ) 33 | 34 | try: 35 | response = api_route.route(http_request) 36 | 37 | except IntegrityError: 38 | https_error = HttpErrors.error_409() 39 | return HttpResponse( 40 | status_code=https_error["status_code"], body=https_error["body"] 41 | ) 42 | except: 43 | https_error = HttpErrors.error_500() 44 | return HttpResponse( 45 | status_code=https_error["status_code"], body=https_error["body"] 46 | ) 47 | return response 48 | -------------------------------------------------------------------------------- /src/main/composer/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_pet_composite import find_pet_composer 2 | from .find_user_composite import find_user_composer 3 | from .register_pet_composite import register_pet_composer 4 | from .register_user_composite import register_user_composer 5 | -------------------------------------------------------------------------------- /src/main/composer/find_pet_composite.py: -------------------------------------------------------------------------------- 1 | from src.presentation import FindPetRouter 2 | from src.data import FindPet 3 | from src.infra import PetRepository 4 | 5 | 6 | def find_pet_composer() -> FindPetRouter: 7 | ''' Composing Find Pet Route 8 | :param - None 9 | :return - Object with Find Pet Route 10 | ''' 11 | 12 | repository = PetRepository() 13 | use_case = FindPet(repository) 14 | find_pet_router = FindPetRouter(use_case) 15 | 16 | return find_pet_router 17 | -------------------------------------------------------------------------------- /src/main/composer/find_user_composite.py: -------------------------------------------------------------------------------- 1 | from src.presentation import FindUserRouter 2 | from src.data import FindUser 3 | from src.infra import UserRepository 4 | 5 | 6 | def find_user_composer() -> FindUserRouter: 7 | ''' Composing Find User Route 8 | :param - None 9 | :return - Object with Find User Route 10 | ''' 11 | 12 | repository = UserRepository() 13 | use_case = FindUser(repository) 14 | find_user_route = FindUserRouter(use_case) 15 | 16 | return find_user_route 17 | -------------------------------------------------------------------------------- /src/main/composer/register_pet_composite.py: -------------------------------------------------------------------------------- 1 | from src.presentation import RegisterPetRoute 2 | from src.data import RegisterPet, FindUser 3 | from src.infra import PetRepository, UserRepository 4 | 5 | 6 | def register_pet_composer() -> RegisterPetRoute: 7 | ''' Composing Register Pet Route 8 | :param - None 9 | :return - Object with Register Pet Route 10 | ''' 11 | 12 | repository = PetRepository() 13 | find_user = FindUser(UserRepository()) 14 | use_case = RegisterPet(repository, find_user) 15 | register_pet_route = RegisterPetRoute(use_case) 16 | 17 | return register_pet_route 18 | -------------------------------------------------------------------------------- /src/main/composer/register_user_composite.py: -------------------------------------------------------------------------------- 1 | from src.presentation import RegisterUserRouter 2 | from src.data import RegisterUser 3 | from src.infra import UserRepository 4 | 5 | def register_user_composer() -> RegisterUserRouter: 6 | ''' Composing Register User Route 7 | :param - None 8 | :return - Object with Register User Route 9 | ''' 10 | 11 | repository = UserRepository() 12 | use_case = RegisterUser(repository) 13 | register_user_route = RegisterUserRouter(use_case) 14 | 15 | return register_user_route 16 | -------------------------------------------------------------------------------- /src/main/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .app import app -------------------------------------------------------------------------------- /src/main/config/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_cors import CORS 3 | from src.main.routes import api_routes_bp 4 | 5 | app = Flask(__name__) 6 | CORS(app) 7 | 8 | app.register_blueprint(api_routes_bp) 9 | -------------------------------------------------------------------------------- /src/main/interface/__init__.py: -------------------------------------------------------------------------------- 1 | from .route import RouteInterface 2 | -------------------------------------------------------------------------------- /src/main/interface/route.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from abc import ABC, abstractmethod 3 | from src.presentation.helpers import HttpRequest, HttpResponse 4 | 5 | class RouteInterface(ABC): 6 | """ Interface to Routes """ 7 | 8 | @abstractmethod 9 | def route(self, http_request: Type[HttpRequest]) -> HttpResponse: 10 | """ Defining Route """ 11 | 12 | raise Exception("Should implement method: route") 13 | -------------------------------------------------------------------------------- /src/main/routes/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_routes import api_routes_bp 2 | -------------------------------------------------------------------------------- /src/main/routes/api_routes.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, Blueprint, request 2 | from src.main.adapter import flask_adapter 3 | from src.main.composer import ( 4 | find_user_composer, 5 | find_pet_composer, 6 | register_user_composer, 7 | register_pet_composer, 8 | ) 9 | 10 | api_routes_bp = Blueprint("api_routes", __name__) 11 | 12 | 13 | @api_routes_bp.route("/api/users", methods=["GET"]) 14 | def get_user(): 15 | """ get user route """ 16 | 17 | response = flask_adapter(request=request, api_route=find_user_composer()) 18 | 19 | message = [] 20 | if response.status_code < 300: 21 | # If not error, format the message and return it 22 | 23 | for element in response.body: 24 | message.append( 25 | { 26 | "type": "Users", 27 | "id": element.id, 28 | "attributes": {"name": element.name}, 29 | } 30 | ) 31 | 32 | return jsonify({"data": message}), response.status_code 33 | 34 | # Handling Errors 35 | return ( 36 | jsonify( 37 | { 38 | "error": { 39 | "status": response.status_code, 40 | "title": response.body["error"], 41 | } 42 | } 43 | ), 44 | response.status_code, 45 | ) 46 | 47 | 48 | @api_routes_bp.route("/api/pets/", methods=["GET"]) 49 | def get_pet(): 50 | """ get pet route """ 51 | 52 | response = flask_adapter(request=request, api_route=find_pet_composer()) 53 | 54 | message = [] 55 | if response.status_code < 300: 56 | # If not error, format the message and return it 57 | 58 | for element in response.body: 59 | message.append( 60 | { 61 | "type": "Pets", 62 | "id": element.id, 63 | "attributes": { 64 | "name": element.name, 65 | "specie": element.specie.value, 66 | "age": element.age, 67 | }, 68 | "relationships": { 69 | "Owner": {"type": "Users", "id": element.user_id} 70 | }, 71 | } 72 | ) 73 | 74 | return jsonify({"data": message}), response.status_code 75 | 76 | # Handling Errors 77 | return ( 78 | jsonify( 79 | { 80 | "error": { 81 | "status": response.status_code, 82 | "title": response.body["error"], 83 | } 84 | } 85 | ), 86 | response.status_code, 87 | ) 88 | 89 | 90 | @api_routes_bp.route("/api/users", methods=["POST"]) 91 | def register_user(): 92 | """ register user route """ 93 | 94 | message = {} 95 | response = flask_adapter(request=request, api_route=register_user_composer()) 96 | 97 | if response.status_code < 300: 98 | # If not error, format the message and return it 99 | 100 | message = { 101 | "type": "Users", 102 | "id": response.body.id, 103 | "attributes": {"name": response.body.name}, 104 | } 105 | 106 | return jsonify({"data": message}), response.status_code 107 | 108 | # Handling Errors 109 | return ( 110 | jsonify( 111 | { 112 | "error": { 113 | "status": response.status_code, 114 | "title": response.body["error"], 115 | } 116 | } 117 | ), 118 | response.status_code, 119 | ) 120 | 121 | 122 | @api_routes_bp.route("/api/pets", methods=["POST"]) 123 | def register_pet(): 124 | """ register pet route """ 125 | 126 | message = {} 127 | response = flask_adapter(request=request, api_route=register_pet_composer()) 128 | print(response) 129 | 130 | if response.status_code < 300: 131 | # If not error, format the message and return it 132 | 133 | message = { 134 | "type": "Pets", 135 | "id": response.body.id, 136 | "attributes": { 137 | "name": response.body.name, 138 | "specie": response.body.specie, 139 | "age": response.body.age, 140 | }, 141 | "relationships": {"Owner": {"type": "Users", "id": response.body.user_id}}, 142 | } 143 | 144 | return jsonify({"data": message}), response.status_code 145 | 146 | # Handling Errors 147 | return ( 148 | jsonify( 149 | { 150 | "error": { 151 | "status": response.status_code, 152 | "title": response.body["error"], 153 | } 154 | } 155 | ), 156 | response.status_code, 157 | ) 158 | -------------------------------------------------------------------------------- /src/presentation/__init__.py: -------------------------------------------------------------------------------- 1 | from .controllers import FindPetRouter, FindUserRouter, RegisterPetRoute, RegisterUserRouter 2 | -------------------------------------------------------------------------------- /src/presentation/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_pet_controller import FindPetRouter 2 | from .find_user_controller import FindUserRouter 3 | from .register_pet_controller import RegisterPetRoute 4 | from .register_user_controller import RegisterUserRouter 5 | -------------------------------------------------------------------------------- /src/presentation/controllers/find_pet_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.data import FindPet 4 | from src.presentation.helpers import HttpResponse, HttpRequest 5 | from src.presentation.errors import HttpErrors 6 | 7 | 8 | class FindPetRouter(RouteInterface): 9 | """ Class to define Route to find_pet use case """ 10 | 11 | def __init__(self, find_pet_use_case: Type[FindPet]): 12 | self.find_pet_use_case = find_pet_use_case 13 | 14 | def route(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """ Method to call use case """ 16 | 17 | response = None 18 | 19 | if http_request.query: 20 | # if query in http_request 21 | 22 | query_string_params = http_request.query.keys() 23 | 24 | if "pet_id" in query_string_params and "user_id" in query_string_params: 25 | pet_id = http_request.query["pet_id"] 26 | user_id = http_request.query["user_id"] 27 | response = self.find_pet_use_case.by_pet_id_and_user_id( 28 | pet_id=pet_id, user_id=user_id 29 | ) 30 | 31 | elif ( 32 | "pet_id" in query_string_params and "user_id" not in query_string_params 33 | ): 34 | pet_id = http_request.query["pet_id"] 35 | response = self.find_pet_use_case.by_pet_id(pet_id=pet_id) 36 | 37 | elif ( 38 | "user_id" in query_string_params and "pet_id" not in query_string_params 39 | ): 40 | user_id = http_request.query["user_id"] 41 | response = self.find_pet_use_case.by_user_id(user_id=user_id) 42 | 43 | else: 44 | response = {"Success": False, "Data": None} 45 | 46 | if response["Success"] is False: 47 | https_error = HttpErrors.error_422() 48 | return HttpResponse( 49 | status_code=https_error["status_code"], body=https_error["body"] 50 | ) 51 | 52 | return HttpResponse(status_code=200, body=response["Data"]) 53 | 54 | # If no query in http_request 55 | https_error = HttpErrors.error_400() 56 | return HttpResponse( 57 | status_code=https_error["status_code"], body=https_error["body"] 58 | ) 59 | -------------------------------------------------------------------------------- /src/presentation/controllers/find_pet_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.data.test import FindPetSpy 3 | from src.infra.test import PetRepositorySpy 4 | from src.presentation.helpers import HttpRequest 5 | from .find_pet_controller import FindPetRouter 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_route(): 11 | """ Testing route method in FindPetRouter """ 12 | 13 | find_pet_use_case = FindPetSpy(PetRepositorySpy()) 14 | find_pet_router = FindPetRouter(find_pet_use_case) 15 | attributes = { 16 | "pet_id": faker.random_number(digits=2), 17 | "user_id": faker.random_number(digits=2), 18 | } 19 | 20 | http_request = HttpRequest(query=attributes) 21 | 22 | http_response = find_pet_router.route(http_request) 23 | 24 | # Testing input 25 | assert ( 26 | find_pet_use_case.by_pet_id_and_user_id_param["pet_id"] == attributes["pet_id"] 27 | ) 28 | assert ( 29 | find_pet_use_case.by_pet_id_and_user_id_param["user_id"] 30 | == attributes["user_id"] 31 | ) 32 | 33 | # Testing output 34 | assert http_response.status_code == 200 35 | assert "error" not in http_response.body 36 | 37 | 38 | def test_route_by_pet_id(): 39 | """ Testing route method in FindPetRouter """ 40 | 41 | find_pet_use_case = FindPetSpy(PetRepositorySpy()) 42 | find_pet_router = FindPetRouter(find_pet_use_case) 43 | attributes = {"pet_id": faker.random_number(digits=2)} 44 | 45 | http_request = HttpRequest(query=attributes) 46 | 47 | http_response = find_pet_router.route(http_request) 48 | 49 | # Testing input 50 | assert find_pet_use_case.by_pet_id_param["pet_id"] == attributes["pet_id"] 51 | 52 | # Testing output 53 | assert http_response.status_code == 200 54 | assert "error" not in http_response.body 55 | 56 | 57 | def test_route_by_user_id(): 58 | """ Testing route method in FindPetRouter """ 59 | 60 | find_pet_use_case = FindPetSpy(PetRepositorySpy()) 61 | find_pet_router = FindPetRouter(find_pet_use_case) 62 | attributes = {"user_id": faker.random_number(digits=2)} 63 | 64 | http_request = HttpRequest(query=attributes) 65 | 66 | http_response = find_pet_router.route(http_request) 67 | 68 | # Testing input 69 | assert find_pet_use_case.by_user_id_param["user_id"] == attributes["user_id"] 70 | 71 | # Testing output 72 | assert http_response.status_code == 200 73 | assert "error" not in http_response.body 74 | 75 | 76 | def test_route_error_no_query(): 77 | """ Testing route method in FindPetRouter """ 78 | 79 | find_pet_use_case = FindPetSpy(PetRepositorySpy()) 80 | find_pet_router = FindPetRouter(find_pet_use_case) 81 | 82 | http_request = HttpRequest() 83 | 84 | http_response = find_pet_router.route(http_request) 85 | 86 | # Testing input 87 | assert find_pet_use_case.by_pet_id_param == {} 88 | assert find_pet_use_case.by_user_id_param == {} 89 | assert find_pet_use_case.by_pet_id_and_user_id_param == {} 90 | 91 | # Testing output 92 | assert http_response.status_code == 400 93 | assert "error" in http_response.body 94 | 95 | 96 | def test_route_error_wrong_query(): 97 | """ Testing route method in FindPetRouter """ 98 | 99 | find_pet_use_case = FindPetSpy(PetRepositorySpy()) 100 | find_pet_router = FindPetRouter(find_pet_use_case) 101 | 102 | http_request = HttpRequest(query={"something": faker.random_number(digits=2)}) 103 | 104 | http_response = find_pet_router.route(http_request) 105 | 106 | # Testing input 107 | assert find_pet_use_case.by_pet_id_param == {} 108 | assert find_pet_use_case.by_user_id_param == {} 109 | assert find_pet_use_case.by_pet_id_and_user_id_param == {} 110 | 111 | # Testing output 112 | assert http_response.status_code == 422 113 | assert "error" in http_response.body 114 | -------------------------------------------------------------------------------- /src/presentation/controllers/find_user_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.data import FindUser 4 | from src.presentation.helpers import HttpRequest, HttpResponse 5 | from src.presentation.errors import HttpErrors 6 | 7 | 8 | class FindUserRouter(RouteInterface): 9 | """ Class to Define Route to find_user use case """ 10 | 11 | def __init__(self, find_user_use_case: Type[FindUser]): 12 | self.find_user_use_case = find_user_use_case 13 | 14 | def route(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """ Method to call use case """ 16 | 17 | response = None 18 | 19 | if http_request.query: 20 | # if query in http_request 21 | 22 | query_string_params = http_request.query.keys() 23 | 24 | if "user_id" in query_string_params and "user_name" in query_string_params: 25 | user_id = http_request.query["user_id"] 26 | user_name = http_request.query["user_name"] 27 | response = self.find_user_use_case.by_id_and_user( 28 | user_id=user_id, name=user_name 29 | ) 30 | 31 | elif ( 32 | "user_id" in query_string_params 33 | and "user_name" not in query_string_params 34 | ): 35 | user_id = http_request.query["user_id"] 36 | response = self.find_user_use_case.by_id(user_id=user_id) 37 | 38 | elif ( 39 | "user_name" in query_string_params 40 | and "user_id" not in query_string_params 41 | ): 42 | user_name = http_request.query["user_name"] 43 | response = self.find_user_use_case.by_name(name=user_name) 44 | 45 | else: 46 | response = {"Success": False, "Data": None} 47 | 48 | if response["Success"] is False: 49 | https_error = HttpErrors.error_422() 50 | return HttpResponse( 51 | status_code=https_error["status_code"], body=https_error["body"] 52 | ) 53 | 54 | return HttpResponse(status_code=200, body=response["Data"]) 55 | 56 | # If no query in http_request 57 | https_error = HttpErrors.error_400() 58 | return HttpResponse( 59 | status_code=https_error["status_code"], body=https_error["body"] 60 | ) 61 | -------------------------------------------------------------------------------- /src/presentation/controllers/find_user_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.data.test import FindUserSpy 3 | from src.infra.test import UserRepositorySpy 4 | from src.presentation.helpers import HttpRequest 5 | from .find_user_controller import FindUserRouter 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_route(): 11 | """ Testing route method in FindUserRouter """ 12 | 13 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 14 | find_user_router = FindUserRouter(find_user_use_case) 15 | attributes = {"user_id": faker.random_number(digits=2), "user_name": faker.word()} 16 | 17 | response = find_user_router.route(HttpRequest(query=attributes)) 18 | 19 | # Testing input 20 | assert find_user_use_case.by_id_and_user_param["user_id"] == attributes["user_id"] 21 | assert find_user_use_case.by_id_and_user_param["name"] == attributes["user_name"] 22 | 23 | # Testing output 24 | assert response.status_code == 200 25 | assert response.body 26 | 27 | 28 | def test_route_by_id(): 29 | """ Testing route method in FindUserRouter """ 30 | 31 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 32 | find_user_router = FindUserRouter(find_user_use_case) 33 | attributes = {"user_id": faker.random_number(digits=2)} 34 | 35 | response = find_user_router.route(HttpRequest(query=attributes)) 36 | 37 | # Testing input 38 | assert find_user_use_case.by_id_param["user_id"] == attributes["user_id"] 39 | 40 | # Testing output 41 | assert response.status_code == 200 42 | assert response.body 43 | 44 | 45 | def test_route_by_name(): 46 | """ Testing route method in FindUserRouter """ 47 | 48 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 49 | find_user_router = FindUserRouter(find_user_use_case) 50 | attributes = {"user_name": faker.word()} 51 | 52 | response = find_user_router.route(HttpRequest(query=attributes)) 53 | 54 | # Testing input 55 | assert find_user_use_case.by_name_param["name"] == attributes["user_name"] 56 | 57 | # Testing Output 58 | assert response.status_code == 200 59 | assert response.body 60 | 61 | 62 | def test_route_error_no_query(): 63 | """ Testing route method in FindUserRouter """ 64 | 65 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 66 | find_user_router = FindUserRouter(find_user_use_case) 67 | 68 | response = find_user_router.route(HttpRequest()) 69 | 70 | # Testing input 71 | assert find_user_use_case.by_id_param == {} 72 | assert find_user_use_case.by_name_param == {} 73 | assert find_user_use_case.by_id_and_user_param == {} 74 | 75 | # Testing output 76 | assert response.status_code == 400 77 | assert "error" in response.body 78 | 79 | 80 | def test_route_error_wrong_query(): 81 | """ Testing route method in FindUserRouter """ 82 | 83 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 84 | find_user_router = FindUserRouter(find_user_use_case) 85 | 86 | response = find_user_router.route(HttpRequest(query={"something": faker.word()})) 87 | 88 | # Testing input 89 | assert find_user_use_case.by_id_param == {} 90 | assert find_user_use_case.by_name_param == {} 91 | assert find_user_use_case.by_id_and_user_param == {} 92 | 93 | # Testing output 94 | assert response.status_code == 422 95 | assert "error" in response.body 96 | -------------------------------------------------------------------------------- /src/presentation/controllers/register_pet_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.data import RegisterPet 4 | from src.presentation.helpers import HttpRequest, HttpResponse 5 | from src.presentation.errors import HttpErrors 6 | 7 | 8 | class RegisterPetRoute(RouteInterface): 9 | """ Class to Define Route to register_pet use case """ 10 | 11 | def __init__(self, register_pet_use_case: Type[RegisterPet]): 12 | self.register_pet_use_case = register_pet_use_case 13 | 14 | def route(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """ Method to call use case """ 16 | 17 | response = None 18 | 19 | if http_request.body: 20 | # if body in htp_request 21 | 22 | body_params = http_request.body.keys() 23 | 24 | if ( 25 | "name" in body_params 26 | and "specie" in body_params 27 | and "user_information" in body_params 28 | ): 29 | # if body param contain correct items 30 | 31 | user_information_params = http_request.body["user_information"].keys() 32 | if ( 33 | "user_id" in user_information_params 34 | or "user_name" in user_information_params 35 | ): 36 | # if user_information contain correct items 37 | 38 | name = http_request.body["name"] 39 | specie = http_request.body["specie"] 40 | user_information = http_request.body["user_information"] 41 | 42 | if "age" in body_params: 43 | age = http_request.body["age"] 44 | else: 45 | age = None 46 | 47 | response = self.register_pet_use_case.registry( 48 | name=name, 49 | specie=specie, 50 | user_information=user_information, 51 | age=age, 52 | ) 53 | 54 | else: 55 | response = {"Success": False, "Data": None} 56 | 57 | else: 58 | response = {"Success": False, "Data": None} 59 | 60 | if response["Success"] is False: 61 | https_error = HttpErrors.error_422() 62 | return HttpResponse( 63 | status_code=https_error["status_code"], body=https_error["body"] 64 | ) 65 | 66 | return HttpResponse(status_code=201, body=response["Data"]) 67 | 68 | # If no body in http_request 69 | https_error = HttpErrors.error_400() 70 | return HttpResponse( 71 | status_code=https_error["status_code"], body=https_error["body"] 72 | ) 73 | -------------------------------------------------------------------------------- /src/presentation/controllers/register_pet_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.data.test import RegisterPetSpy 3 | from src.presentation.helpers import HttpRequest 4 | from src.infra.test import PetRepositorySpy, UserRepositorySpy 5 | from .register_pet_controller import RegisterPetRoute 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_route(): 11 | """ Testing route method in RegisterUserRouter """ 12 | 13 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 14 | register_pet_router = RegisterPetRoute(register_pet_use_case) 15 | attributes = { 16 | "name": faker.word(), 17 | "specie": "Dog", 18 | "age": faker.random_number(), 19 | "user_information": { 20 | "user_id": faker.random_number(), 21 | "user_name": faker.word(), 22 | }, 23 | } 24 | 25 | response = register_pet_router.route(HttpRequest(body=attributes)) 26 | 27 | # Testing input 28 | assert register_pet_use_case.registry_param["name"] == attributes["name"] 29 | assert register_pet_use_case.registry_param["specie"] == attributes["specie"] 30 | assert register_pet_use_case.registry_param["age"] == attributes["age"] 31 | assert ( 32 | register_pet_use_case.registry_param["user_information"] 33 | == attributes["user_information"] 34 | ) 35 | 36 | # Testing output 37 | assert response.status_code == 201 38 | assert "error" not in response.body 39 | 40 | 41 | def test_route_without_age(): 42 | """ Testing route method in RegisterUserRouter """ 43 | 44 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 45 | register_pet_router = RegisterPetRoute(register_pet_use_case) 46 | attributes = { 47 | "name": faker.word(), 48 | "specie": "Dog", 49 | "user_information": { 50 | "user_id": faker.random_number(), 51 | "user_name": faker.word(), 52 | }, 53 | } 54 | 55 | response = register_pet_router.route(HttpRequest(body=attributes)) 56 | 57 | # Testing input 58 | assert register_pet_use_case.registry_param["name"] == attributes["name"] 59 | assert register_pet_use_case.registry_param["specie"] == attributes["specie"] 60 | assert register_pet_use_case.registry_param["age"] is None 61 | assert ( 62 | register_pet_use_case.registry_param["user_information"] 63 | == attributes["user_information"] 64 | ) 65 | 66 | # Testing output 67 | assert response.status_code == 201 68 | assert "error" not in response.body 69 | 70 | 71 | def test_route_user_id_in_user_information(): 72 | """ Testing route method in RegisterUserRouter """ 73 | 74 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 75 | register_pet_router = RegisterPetRoute(register_pet_use_case) 76 | 77 | attributes = { 78 | "name": faker.word(), 79 | "specie": "Dog", 80 | "user_information": {"user_name": faker.word()}, 81 | } 82 | 83 | response = register_pet_router.route(HttpRequest(body=attributes)) 84 | 85 | # Testing input 86 | assert register_pet_use_case.registry_param["name"] == attributes["name"] 87 | assert register_pet_use_case.registry_param["specie"] == attributes["specie"] 88 | assert register_pet_use_case.registry_param["age"] is None 89 | assert ( 90 | register_pet_use_case.registry_param["user_information"] 91 | == attributes["user_information"] 92 | ) 93 | 94 | # Testing output 95 | assert response.status_code == 201 96 | assert "error" not in response.body 97 | 98 | 99 | def test_route_error_no_body(): 100 | """ Testing route method in RegisterUserRouter """ 101 | 102 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 103 | register_pet_router = RegisterPetRoute(register_pet_use_case) 104 | 105 | response = register_pet_router.route(HttpRequest()) 106 | 107 | # Testing input 108 | assert register_pet_use_case.registry_param == {} 109 | 110 | # Testing output 111 | assert response.status_code == 400 112 | assert "error" in response.body 113 | 114 | 115 | def test_route_error_wrong_body(): 116 | """ Testing route method in RegisterUserRouter """ 117 | 118 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 119 | register_pet_router = RegisterPetRoute(register_pet_use_case) 120 | 121 | attributes = { 122 | "specie": "Dog", 123 | "user_information": { 124 | "user_id": faker.random_number(), 125 | "user_name": faker.word(), 126 | }, 127 | } 128 | 129 | response = register_pet_router.route(HttpRequest(body=attributes)) 130 | 131 | # Testing input 132 | assert register_pet_use_case.registry_param == {} 133 | 134 | # Testing output 135 | assert response.status_code == 422 136 | assert "error" in response.body 137 | 138 | 139 | def test_route_error_wrong_user_information(): 140 | """ Testing route method in RegisterUserRouter """ 141 | 142 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), UserRepositorySpy()) 143 | register_pet_router = RegisterPetRoute(register_pet_use_case) 144 | 145 | attributes = {"name": faker.word(), "specie": "Dog", "user_information": {}} 146 | 147 | response = register_pet_router.route(HttpRequest(body=attributes)) 148 | 149 | # Testing input 150 | assert register_pet_use_case.registry_param == {} 151 | 152 | # Testing output 153 | assert response.status_code == 422 154 | assert "error" in response.body 155 | -------------------------------------------------------------------------------- /src/presentation/controllers/register_user_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.data import RegisterUser 4 | from src.presentation.helpers import HttpRequest, HttpResponse 5 | from src.presentation.errors import HttpErrors 6 | 7 | 8 | class RegisterUserRouter(RouteInterface): 9 | """ Class to Define Route to register_user use case """ 10 | 11 | def __init__(self, register_user_use_case: Type[RegisterUser]): 12 | self.register_user_use_case = register_user_use_case 13 | 14 | def route(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """ Method to call use case """ 16 | 17 | response = None 18 | 19 | if http_request.body: 20 | # if body in htp_request 21 | 22 | body_params = http_request.body.keys() 23 | 24 | if "name" in body_params and "password" in body_params: 25 | name = http_request.body["name"] 26 | password = http_request.body["password"] 27 | response = self.register_user_use_case.registry( 28 | name=name, password=password 29 | ) 30 | 31 | else: 32 | response = {"Success": False, "Data": None} 33 | 34 | if response["Success"] is False: 35 | https_error = HttpErrors.error_422() 36 | return HttpResponse( 37 | status_code=https_error["status_code"], body=https_error["body"] 38 | ) 39 | 40 | return HttpResponse(status_code=201, body=response["Data"]) 41 | 42 | # If no body in http_request 43 | https_error = HttpErrors.error_400() 44 | return HttpResponse( 45 | status_code=https_error["status_code"], body=https_error["body"] 46 | ) 47 | -------------------------------------------------------------------------------- /src/presentation/controllers/register_user_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.data.test import RegisterUserSpy 3 | from src.presentation.helpers import HttpRequest 4 | from src.infra.test import UserRepositorySpy 5 | from .register_user_controller import RegisterUserRouter 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_route(): 11 | """ Testing route method in RegisterUserRouter """ 12 | 13 | register_user_use_case = RegisterUserSpy(UserRepositorySpy()) 14 | register_user_router = RegisterUserRouter(register_user_use_case) 15 | attributes = {"name": faker.word(), "password": faker.word()} 16 | 17 | response = register_user_router.route(HttpRequest(body=attributes)) 18 | 19 | # Testing input 20 | assert register_user_use_case.registry_param["name"] == attributes["name"] 21 | assert register_user_use_case.registry_param["password"] == attributes["password"] 22 | 23 | # Testing output 24 | assert response.status_code == 201 25 | assert "error" not in response.body 26 | 27 | 28 | def test_route_error_no_body(): 29 | """ Testing route method in RegisterUserRouter """ 30 | 31 | register_user_use_case = RegisterUserSpy(UserRepositorySpy()) 32 | register_user_router = RegisterUserRouter(register_user_use_case) 33 | 34 | response = register_user_router.route(HttpRequest()) 35 | 36 | # Testing input 37 | assert register_user_use_case.registry_param == {} 38 | 39 | # Testing output 40 | assert response.status_code == 400 41 | assert "error" in response.body 42 | 43 | 44 | def test_route_error_wrong_body(): 45 | """ Testing route method in RegisterUserRouter """ 46 | 47 | register_user_use_case = RegisterUserSpy(UserRepositorySpy()) 48 | register_user_router = RegisterUserRouter(register_user_use_case) 49 | attributes = {"name": faker.word()} 50 | 51 | response = register_user_router.route(HttpRequest(body=attributes)) 52 | 53 | # Testing input 54 | assert register_user_use_case.registry_param == {} 55 | 56 | # Testing output 57 | assert response.status_code == 422 58 | assert "error" in response.body 59 | -------------------------------------------------------------------------------- /src/presentation/errors/__init__.py: -------------------------------------------------------------------------------- 1 | from .http_errors import HttpErrors 2 | -------------------------------------------------------------------------------- /src/presentation/errors/http_errors.py: -------------------------------------------------------------------------------- 1 | class HttpErrors: 2 | """ Class to define http errors """ 3 | 4 | @staticmethod 5 | def error_400(): 6 | """ Http error 400 - Bad Request """ 7 | 8 | return {"status_code": 400, "body": {"error": "Bad Request"}} 9 | 10 | @staticmethod 11 | def error_404(): 12 | """ Http error 404 - Not Found """ 13 | 14 | return {"status_code": 404, "body": {"error": "Not Found"}} 15 | 16 | @staticmethod 17 | def error_409(): 18 | """ Http error 409 - Conflict """ 19 | 20 | return {"status_code": 409, "body": {"error": "Conflict"}} 21 | 22 | @staticmethod 23 | def error_422(): 24 | """ Http error 422 - Unprocessable Entity """ 25 | 26 | return {"status_code": 422, "body": {"error": "Unprocessable Entity"}} 27 | 28 | @staticmethod 29 | def error_500(): 30 | """ Http error 500 - Internal Server Error """ 31 | 32 | return {"status_code": 500, "body": {"error": "Internal Server Error"}} 33 | -------------------------------------------------------------------------------- /src/presentation/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | from .http_models import HttpRequest, HttpResponse 2 | -------------------------------------------------------------------------------- /src/presentation/helpers/http_models.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | 4 | class HttpRequest: 5 | """ Class to http_request representation """ 6 | 7 | def __init__(self, header: Dict = None, body: Dict = None, query: Dict = None): 8 | self.header = header 9 | self.body = body 10 | self.query = query 11 | 12 | def __repr__(self): 13 | return ( 14 | f"HttpRequest (header={self.header}, body={self.body}, query={self.query})" 15 | ) 16 | 17 | 18 | class HttpResponse: 19 | """ Class to http_response representation """ 20 | 21 | def __init__(self, status_code: int, body: any): 22 | self.status_code = status_code 23 | self.body = body 24 | 25 | def __repr__(self): 26 | return f"HttpResponse (status_code={self.status_code}, body={self.body})" 27 | -------------------------------------------------------------------------------- /storage.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programadorLhama/backend_project_python-lab-/95ab11b8be3edca3d34888708b41bd2acac3d3e7/storage.db --------------------------------------------------------------------------------