# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['open_alchemy', 'open_alchemy.package_database']

package_data = \
{'': ['*']}

install_requires = \
['packaging>=20.8,<21.0', 'pynamodb>=4.3.3,<5.0.0']

entry_points = \
{'pytest11': ['open_alchemy_package_database = '
              'open_alchemy.package_database.pytest_plugin']}

setup_kwargs = {
    'name': 'open-alchemy.package-database',
    'version': '3.0.0',
    'description': 'Facade for the OpenAlchemy package database',
    'long_description': '# Database\n\nThe database has a facade exposes a series of functions that enable services to\npersonalize responses.\n\n## Interface\n\nThe interface for the database is defined as the `TDatabase` class here:\n[open_alchemy/package_database/types.py](open_alchemy/package_database/types.py)\n\nIt can be retrieved using:\n\n```python\nfrom open_alchemy import package_database\n\ndatabase_instance = package_database.get()\n```\n\nNote that the `STAGE` environment variable needs to be set. The possible\nvalues are:\n\n- `TEST`: DynamoDB is assumed to be running at <http://localhost:8000>\n- `PROD`: a connection is established to the AWS hosted DynamoDB\n\n## Tables\n\n### Specs\n\nStores information about the specs for a user. The following access patterns\nare expected:\n\n- count the number of models for a user,\n- create or update a spec record for a user,\n- get the latest version of a spec for a user,\n- list all specs for a user,\n- retrieve a particular spec for a user,\n- delete a particular spec for a user,\n- list all versions of a spec for a user and\n- delete all specs for a user.\n\n#### Count Models for a User\n\nCounts the number of models a user has defined.\n\nInput:\n\n- `sub`: unique identifier for the user.\n\nOutput:\n\n- The sum of the latest `model_count` for each spec for the user.\n\nAlgorithm:\n\n1. filter by the `sub` and `updated_at_id` to start with `latest#` and\n1. sum over the `model_count` of each record.\n\n#### Create or Update a Spec\n\nInput:\n\n- `sub`,\n- `name`: the name of the spec,\n- `version`: the version of the spec,\n- `model_count`: the number of models in the spec,\n- `title` (_optional_): the title of the spec and\n- `description` (_optional_): the description of the spec.\n\nOutput:\n\nAlgorithm:\n\n1. calculate the `id` of the spec using\n   <https://packaging.pypa.io/en/latest/utils.html#packaging.utils.canonicalize_name>\n   based on the `name`,\n1. calculate `updated_at` based on he current EPOCH time using\n   <https://docs.python.org/3/library/time.html#time.time>\n   and convert to an integer represented as a string,\n1. calculate the value for `updated_at_id` by joining a zero padded\n   `updated_at` to 20 characters and `id` with a `#` and for `id_updated_at`\n   by joining `id` and `updated_at` with a `#`,\n1. save the item to the database,\n1. create another item but use `latest` for `updated_at` when generating\n   `updated_at_id` and `id_updated_at`\n\n#### Get Latest Spec Version\n\nRetrieve the latest version of a spec.\n\nInput:\n\n- `sub` and\n- `name`.\n\nOutput:\n\n- The latest `version` of the spec.\n\nAlgorithm:\n\n1. calculate the `id` of the spec using\n   <https://packaging.pypa.io/en/latest/utils.html#packaging.utils.canonicalize_name>\n   based on the `name`,\n1. Retrieve the item using the `sub` partition key and `updated_at_id` sort key\n   equal to `latest#<id>` and\n1. return the version of the item.\n\n#### List Specs\n\nReturns information about all the available specs for a user.\n\nInput:\n\n- `sub`.\n\nOutput:\n\n- A list of dictionaries with the `id`, `name`, `updated_at`, `version`,\n  `model_count` and `title` and `description` if they are defined.\n\nAlgorithm:\n\n1. filter items using the `sub` partition key and `updated_at_id` starting with\n   `latest#` and\n1. convert the items to dictionaries.\n\n#### Get Spec\n\nRetrieve a particular spec for a user.\n\nInput:\n\n- `sub` and\n- `name`.\n\nOutput:\n\n- dictionary with the `id`, `name`, `updated_at`, `version`, `model_count` and\n  `title` and `description` if they are defined\n\nAlgorithm:\n\n1. calculate the `id` of the spec using\n   <https://packaging.pypa.io/en/latest/utils.html#packaging.utils.canonicalize_name>\n   based on the `name`,\n1. get the item based on `sub` and `updated_at_id` with the value\n   `latest#<id>`,\n1. handle the case where it is not found by returning `None` and\n1. convert the item to a dictionary.\n\n#### Delete Spec\n\nDelete a particular spec for a user.\n\nInput:\n\n- `sub` and\n- `name`.\n\nOutput:\n\nAlgorithm:\n\n1. calculate the `id` of the spec using\n   <https://packaging.pypa.io/en/latest/utils.html#packaging.utils.canonicalize_name>\n   based on the `name`,\n1. query the `id_updated_at_index` local secondary index by filtering for `sub`\n   and `id_updated_at` starting with `<id>#` and\n1. delete all returned items.\n\n#### List Spec Versions\n\nReturns information about all the available versions of a spec for a user.\n\nInput:\n\n- `sub` and\n- `name`.\n\nOutput:\n\n- A list of dictionaries with the `id`, `name`, `updated_at`, `version`,\n  `model_count` and `title` and `description` if they are defined.\n\nAlgorithm:\n\n1. calculate the `id` of the spec using\n   <https://packaging.pypa.io/en/latest/utils.html#packaging.utils.canonicalize_name>\n   based on the `name`,\n1. query the `id_updated_at_index` local secondary index by filtering for `sub`\n   and `id_updated_at` starting with `<id>#`,\n1. filter out any items where `updated_at_id` starts with `latest#` and\n1. convert the items to dictionaries.\n\n#### Delete All Specs for a User\n\nInput:\n\n- `sub`.\n\nOutput:\n\nAlgorithm:\n\n1. Delete all entries for `sub`.\n\n#### Spec Properties\n\n- `sub`: A string that is the partition key of the table.\n- `id`: A string.\n- `name`: A string.\n- `updated_at`: A string.\n- `version`: A string.\n- `title`: An optional string.\n- `description`: An optional string.\n- `model_count` A number.\n- `updated_at_id`: A string that is the sort key of the table.\n- `id_updated_at`: A string that is the sort key of the\n  `idUpdatedAt` local secondary index of the table.\n\n### Credentials\n\nStores credentials for a user. The following access patterns are expected:\n\n- list available credentials for a user,\n- create or update credentials for a user,\n- retrieve particular credentials for a user,\n- check that a public and secret key combination exists and retrieve the `sub`\n  for it,\n- delete particular credentials for a user and\n- delete all credentials for a user.\n\n#### List Credentials\n\nList all available credentials for a user.\n\nInput:\n\n- `sub`.\n\nOutput:\n\n- list of dictionaries with the `id`, `public_key` and `salt` keys.\n\nAlgorithm:\n\n1. use the `sub` partition key to retrieve all credentials for the user and\n1. map the items to a dictionary.\n\n#### Create or Update Credentials\n\nCreate or update credentials for a user.\n\nInput:\n\n- `sub`: unique identifier for the user,\n- `id`: unique identifier for the credentials,\n- `public_key`: public identifier for the credentials,\n- `secret_key_hash`: a hash of the secret key for the credentials that is safe\n  to store,\n- `salt`: a random value used to generate the credentials.\n\nOutput:\n\nAlgorithm:\n\n1. create and store an item based on the input.\n\n#### Retrieve Credentials\n\nIf the credential with the id exists, return it. Otherwise, return `None`.\n\nInput:\n\n- `sub`: unique identifier for the user and\n- `id`: unique identifier for the credential.\n\nOutput:\n\n- `id`,\n- `public_key`,\n- `salt`.\n\nAlgorithm:\n\n1. Use the `sub` partition key and `id` sort key to check whether\n   an entry exists,\n1. if an entry exists, return the `public_key` and `salt` and\n1. return `None`.\n\n#### Retrieve User\n\nCheck that the public key exists and retrieve the user and salt for it.\n\nInput:\n\n- `public_key`.\n\nOutput:\n\n- `sub`,\n- `salt` and\n- `secret_key_hash`.\n\nAlgorithm:\n\n1. check whether an entry exists using the `public_key` partition key for the\n   `publicKey` global secondary index\n1. if it does not exist, return `None` and\n1. retrieve and return the `sub`, `salt` and `secret_key_hash`.\n\n#### Delete a Credential for a User\n\nInput:\n\n- `sub` and\n- `id`.\n\nOutput:\n\nAlgorithm:\n\n1. Delete all entries for `sub` and `id`.\n\n#### Delete All Credentials for a User\n\nInput:\n\n- `sub`.\n\nOutput:\n\nAlgorithm:\n\n1. Delete all entries for `sub`.\n\n#### Credentials Properties\n\n- `sub`: A string that is the partition key of the table.\n- `id`: A string that is the sort key of the table.\n- `public_key`: A string that is the partition key of the `publicKey`\n  global secondary index.\n- `secret_key_hash`: Bytes.\n- `salt`: Bytes.\n\n### `test`\n\nExecutes the tests defined at [tests](tests).\n\n### `build`\n\nBuilds the database package defined at [.](.).\n\n### `release-required`\n\nHas 2 outputs:\n\n- `result`: whether a release to PyPI is required based on the latest released\n  version and the version configured in the project and\n- `project-version`: the version configured in the code base.\n\n### `deploy`\n\nDeploys the CloudFormation stack for the database defined at\n[../infrastructure/lib/database-stack.ts](../infrastructure/lib/database-stack.ts).\n\n### `release`\n\nIf the `result` output from `release-required` is true, the package is deployed\nto both test and production PyPI.\n\nIrrespective of whether the release was executed, the version of the package\ndefined in the code base is installed from both test and production PyPI and\nthe tests defined at [../test/database/tests](../test/database/tests) are\nexecuted against the deployed infrastructure on AWS.\n\n## Periodic Production Tests\n\nThe workflow is defined here:\n[../.github/workflows/production-test-database.yaml](../.github/workflows/production-test-database.yaml).\n\nExecutes the tests defined at [../test/database/tests](../test/database/tests)\nagainst a configured version of the package and against the currently deployed\ninfrastructure on AWS.\n\n## Pytest Plugin\n\nA pytest plugin is made available to make testing easier. It is defined at\n[open_alchemy/package_database/pytest_plugin.py](open_alchemy/package_database/pytest_plugin.py).\n\nIt requires the `dynalite` NPM package to be installed in the project using\n`npm install --save-dev dynalite` to run a local dynamoDB instance at\n<http://localhost:8000>. More information on the package is here:\n<https://www.npmjs.com/package/dynalite>.\n\n### Fixtures\n\nAll fixtures that have an effect but yield `None` are prefixed with `_` so that\ntools like pylint do not complain about unused arguments for test functions.\n\n#### `_database`\n\nSpins up the database at the start of the tests and tars down the database at\nthe end. This fixture is unlikely to be useful as no tables are created. The\ntable specific fixtures depend on the `_database` fixture so it is not\nnecessary to include this fixture in any tests.\n\n#### `_specs_table`\n\nCreates the `package.specs` table before all the tests and deletes it after all\ntests complete.\n\n#### `_clean_specs_table`\n\nDeletes all items from the `package.specs` table before and after each test.\n\n#### `_credentials_table`\n\nCreates the `package.credentials` table before all the tests and deletes it after\nall tests complete.\n\n#### `_clean_credentials_table`\n\nDeletes all items from the `package.credentials` table before and after each\ntest.\n\n## Infrastructure\n\nThe CloudFormation stack is defined here:\n[../infrastructure/lib/database-stack.ts](../infrastructure/lib/database-stack.ts).\n\n## CI-CD\n\nThe workflow is defined here:\n[../.github/workflows/ci-cd-database.yaml](../.github/workflows/ci-cd-database.yaml).\n\nThere are a few groups of jobs in the CI-CD:\n\n- `test`: runs the tests for the package in supported python versions,\n- `build`: builds the database package,\n- `deploy`: deploys database infrastructure to AWS,\n- `release-required`: determines whether a release to PyPI is required and\n- `release`: a combination of deploying to test and production PyPI and\n  executing tests on the published packages\n\n## Production Tests\n\nThe tests against the deployed database facade are defined here:\n[../test/database/](../test/database/).\n\nThe workflow that periodically executes the tests is defined here:\n[../.github/workflows/production-test-database.yaml](../.github/workflows/production-test-database.yaml).\n',
    'author': 'David Andersson',
    'author_email': 'jdkandersson@users.noreply.github.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
