gallagher


Namegallagher JSON
Version 0.1.0a5 PyPI version JSON
download
home_pagehttps://github.com/anomaly/gallagher
SummaryThe missing developer toolkit for Gallagher Command Centre
upload_time2024-08-25 22:52:50
maintainerNone
docs_urlNone
authorDev Mukherjee
requires_python<4.0,>=3.11
licenseMIT
keywords gallagher rest api tui cli
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Gallagher Python Toolkit

> The missing developer toolkit for Gallagher Command Centre

[![PyPI version](https://badge.fury.io/py/gallagher.svg)](https://badge.fury.io/py/gallagher)
[![Python Version](https://img.shields.io/pypi/pyversions/gallagher)](https://pypi.org/project/gallagher/)
[![Build Status](https://github.com/anomaly/gallagher/actions/workflows/run-tests.yml/badge.svg?branch=master)](https://github.com/anomaly/gallagher/actions?query=branch%3Amaster)

<div align="center">
<img src="https://raw.githubusercontent.com/anomaly/gallagher/master/assets/logo-gpt.png" alt="Gallagher Python Toolkit Logo" height=128 width=128/>
</div>

Gallagher Security manufacture a variety of perimeter [security products](https://security.gallagher.com). At the hear of these is the [Command Centre](https://products.security.gallagher.com/security/au/en_AU/products/software/command-centre/p/C201311) software. Command Centre is deployed locally (in simplistic terms, the complexity varies for every use case). Version `8.6` introduced a REST API which allows you to interact with the system via HTTP requests locally or via Gallagher's [Cloud API Gateway](https://gallaghersecurity.github.io/docs/Command%20Centre%20Cloud%20Api%20Gateway%20TIP.pdf) which eliminates the need for maintaining proxies and VPNs.

Our Python Toolkit focuses on enhancing the developer experience (DX) around the REST API. In principle we provide the following:

- **Python SDK** an idiomatic client (including `asyncio` support) to extend the CC functionality.
- **Command Line Interface** (CLI) to build powerful pipeline-based workflows.
- **Terminal User Interface** (TUI) for easy interactions with the Command Centre.
- **SQL interface** query the REST API as if it were a database or interact with via an ORM.

> [!NOTE]\
> This project is **NOT** affiliated with Gallagher Security. All trademarks are the property of their respective owners.

While Gallagher maintain a set of [Swagger definitions](https://github.com/gallaghersecurity/cc-rest-docs) for their API, they are primarily intended to generate the documentation [published on Github](https://gallaghersecurity.github.io/cc-rest-docs/ref/index.html). They use a tool called [Spectacle](https://github.com/sourcey/spectacle). Gallagher explicitly state that the Swagger definitions are not intended to be used to generate code. Due to this the API client is hand built and not auto-generated.

> [!IMPORTANT]\
> Due to custom annotations the YAML files will not parse with any standard parser.

Everything this project provides hinges upon our Python SDK, designed to enhance the developer experience. It's design is highly opinionated from our experience in building APIs, we ensure conformance with Gallagher software design interfaces.

> [!TIP]\
> If you've worked with [stripe-python](https://github.com/stripe/stripe-python) the syntax may feel familiar.

If you are using one of our user facing tools, it's not important for you to understand how the SDK works, however since it underpins everything, here's a rather sample example:

```python
# Import core python libs
import os
import asyncio

# Import the client and models
from gallagher import (
    cc,
)
from gallagher.dto.summary import (
    CardholderSummary,
)
from gallagher.cc.cardholders import (
    Cardholder,
)

# Set the API key from the environment
api_key = os.environ.get("GACC_API_KEY")
cc.api_key = api_key

# Async support gives us back a coroutine
ch_coro = Cardholder.list()

# Run the coroutine to get the cardholder
cardholders = asyncio.run(ch_coro)
cardholder = cardholders.results[0]

# This is now a pydantic object
type(cardholder) == CardholderSummary

# Print out some details from the object
cardholder.href
cardholder.first_name
```

> [!IMPORTANT]\
> Gallagher infrastructure deals with perimeter security. We take this extremely seriously and providing a complete test suite to provide that our software meets all standards. These tests constantly run against our _demo_ command centre hosted on the cloud.

The rest of the README touches upon each of the tools we provide. If you like what you see so far we recommend you [head over to our documentation](https://anomaly.github.io/gallagher).

## Using the CLI and TUI

Our CLI is designed to automate custom workflows via scripts. Inspired by the greatest Unix tools out there, it does one thing and it does it well, leaving you to integrate it into a pipeline. The utility is able to speaking machine readable formats like JSON, YAML and CSV as well as producing formatted output.

Here's an example of fetching the details of a `cardholder`:

```
(gallagher-py3.11) ➜  gallagher git:(alpha-3) gala ch get 8272
 person
                  id 8272
          first_name Jerry
           last_name Zurcher
          short_name None
         description None
          authorised yes

  disable_cipher_pad no
            division 2
 hrefs
                edit edit
```

## Interacting via SQL

[Shillelagh](https://shillelagh.readthedocs.io/en/latest/) is a Python library that allows you to interact with REST APIs as if they were SQL databases, including the ability to provide a SQLAlchemy `dialect` allowing you to treat endpoints as a virtual table.

Assuming you had the SQL extensions installed, a simplistic example of querying Cardholders from the command would look like this:

```sql
🍀> SELECT * FROM "https://commandcentre-api-au.security.gallagher.cloud/api/cardholders" WHERE id=8427;
```

which would return a result set of:

```
first_name    last_name    authorised    id
------------  -----------  ------------ ----
Cammy         Albares      True         8427
(1 row in 0.23s)

```

## Command Centre API Notes

The Gallagher API the principles of [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) which ensures that the API is self-descriptive and future proof.

A `href` attribute provides a the destination of referenced objects in the responses. These are full qualified and will be prefixed with the server origin i.e if you are using the Cloud Gateway then all your URLs will be prefixed with the appropriate gateway's address.

These appear in various forms, starting from as simple as the `href` itself:

```json
"cardholders": {
    "href": "https://localhost:8904/api/access_groups/352/cardholders"
}
```

through to self recursive references (where the data is nested) with additional attributes:

```json
"parent": {
    "href": "https://localhost:8904/api/access_groups/100",
    "name": "All R&D"
}
```

> [!CAUTION]\
> Following the design patterns outlined by HATEOAS, you must never hardcode any URLs. You should hit the base API URL which returns the `hrefs` of all other resources.
> If you are using the Python SDK, then you don't have to worry about this, the client will handle this for you.

## Python SDK Design

This API client primarily depends on the following libraries:

- [httpx](https://www.python-httpx.org), fo transporting and parsing HTTP requests
- [pydantic](https://pydantic.dev), for validating responses and constructing request bodies

We use [Taskfile](https://taskfile.dev) to automate running tasks.

The project provides a comprehensive set of tests which can be run with `task test`. These tests do create objects in the Command Centre, we advice you to obtain a test license.

> [!IMPORTANT]
> It's **not recommended** to run tests against a production system.

### Data Transfer Objects

There are three types of schema definitions, each one of them suffixed with their intent:

- **Ref** are `References` to other objects, they using contain a `href` and possibly additional meta data such as a `name` or `id`
- **Summary** is what is returned by the Gallagher API in operations such as [searches](https://gallaghersecurity.github.io/cc-rest-docs/ref/cardholders.html), these are generally a subset of the full object
- **Detail** are the full object found at a particular `href`, they compound on the `Summary` schema and add additional attributes
- **Response** is a collection of `Summary` objects with other paths like `next` and `previous` for pagination and `updates` for polling results
- **Payload** is used to send a request to the API

In summary the properties of each are as follows:

- `Refs` are the minimal pathway to an object
- `Summary` builds on a `Ref` and provides a subset of the attributes
- `Detail` builds on a `Summary` and provides the full set of attributes
- `Response` encapsulates a collection of `Summary` objects, they typically have `next` and `previous` paths for pagination
- `Payload` are verbose and match the schema definition on the documentation

Each `resource` endpoint subclasses the `APIEndpoint` which marks a resource as `fetchable`, `queryable`, `creatable`, `updatable` and `deletable`. This is determined by the configuration defined using an `EndpointConfig` class.

> [!TIP]
> The above is meant to be a summary, please see [our documentation](https://anomaly.github.io/gallagher) for more details.

### Schema Design Patterns

Our `schemas` provide a set of `Mixins` that are used to construct the Models. These are repeatable patterns that need not be repeated. The typical patter would be to subclass from the `Mixins` e.g:

```python
from .utils import AppBaseModel, IdentityMixin, HrefMixin

class AccessGroupRef(
    AppBaseModel,
    HrefMixin
):
    """ Access Groups is what a user is assigned to to provide access to doors
    """
    name: str
```

where the `HrefMixin` (see also `OptionalHrefMixin` for use where the `href` is not always present) provides the `href` attribute:

```python
class HrefMixin(BaseModel):
    """ Href

    This mixin is used to define the href field for all
    responses from the Gallagher API.
    """
    href: str
```

These `Mixin` classes can also be used to declare attributes that seek to use the same pattern:

````python
class DivisionDetail(
    AppBaseModel,
    IdentityMixin,
):
    """ Defines a Division on the Gallagher Command Centre
    """

    name: str
    description: Optional[str] = None
    server_display_name: Optional[str] = None
    parent: OptionalHrefMixin = None

### Schemas

Our `schemas` provide a set of `Mixins` that are used to construct the Models. These are repeatable patterns that need not be repeated. The typical patter would be to subclass from the `Mixins` e.g:

```python
from .utils import AppBaseModel, IdentityMixin, HrefMixin

class AccessGroupRef(
    AppBaseModel,
    HrefMixin
):
    """ Access Groups is what a user is assigned to to provide access to doors
    """
    name: str
````

where the `HrefMixin` provides the `href` attribute:

```python
class HrefMixin(BaseModel):
    """ Href

    This mixin is used to define the href field for all
    responses from the Gallagher API.
    """
    href: str
```

These `Mixin` classes can also be used to declare attributes that seek to use the same pattern:

```python
class DivisionDetail(
    AppBaseModel,
    IdentityMixin,
):
    """ Outlines the definition of a Division on the Gallagher Command Centre
    """

    name: str
    description: Optional[str]
    server_display_name: str
    parent: Optional[HrefMixin]
```

where `parent` is simply an `href` without any other attributes. In the cases where these attributes have more than just an `href` we defined `Reference` classes:

```python
class AccessGroupRef(
    AppBaseModel,
    HrefMixin
):
    """ Access Groups is what a user is assigned to to provide access to doors
    """
    name: str
```

and use them to populate the attributes:

```python
class VisitorTypeDetail(
    AppBaseModel,
    IdentityMixin
):
    """
    """
    access_group : AccessGroupRef
    host_access_groups: list[AccessGroupSummary]
    visitor_access_groups: list[AccessGroupSummary]
```

In this example the `AppGroupRef` has a `name` attribute which is not present in the `HrefMixin` class.

> Please see the schema section for naming conventions for `schema` classes

where `parent` is simply an `href` without any other attributes. In the cases where these attributes have more than just an `href` we defined `Reference` classes:

```python
class AccessGroupRef(
    AppBaseModel,
    HrefMixin
):
    """ Access Groups is what a user is assigned to to provide access to doors
    """
    name: str
```

and use them to populate the attributes:

```python
class VisitorTypeDetail(
    AppBaseModel,
    IdentityMixin
):
    """
    """
    access_group : AccessGroupRef
    host_access_groups: list[AccessGroupSummary]
    visitor_access_groups: list[AccessGroupSummary]
```

In this example the `AppGroupRef` has a `name` attribute which is not present in the `HrefMixin` class.

> Please see the schema section for naming conventions for `schema` classes

## Resources

The following are resources that were discoverd during the design and development of these tools. Not all of them are in use by the toolkit, they were discovered as the library evolved.

### Python Libraries

> [!TIP]
> Following are Python libraries that I have found during the development of the Gallagher tools. They are not necessarily in use at the moment but a reference in case we need the functionality.

- [plotext](https://github.com/piccolomo/plotext?tab=readme-ov-file) - plots directly on your terminal (something I found when I was exploring apps like [dolphie](https://github.com/charles-001/dolphie))
- [rich-pixels](https://github.com/darrenburns/rich-pixels) - a [Rich-compatible](https://github.com/Textualize/rich) library for writing pixel images and other colourful grids to the terminal by @darrenburns
- [PyFilesystem](https://github.com/pyfilesystem/pyfilesystem2) - a Python file system abstraction layer

### Articles

- [A year of building for the terminal](https://textual.textualize.io/blog/2022/12/20/a-year-of-building-for-the-terminal/) by [@darrenburns](https://github.com/darrenburns)

## License

Distributed under the MIT License except Artwork and Branding assets.

## Credits

- [Matthew Skiles](https://matthewskiles.com) for the beautiful logo for the project.
- [Orion Edwards](https://github.com/borland) for all his support on getting @devraj started with the Gallagher API.
- [Mick Lambert](https://www.linkedin.com/in/michael-lambert-au/), [Tim Harris](https://www.linkedin.com/in/timharris01/), [Andrew Donkin](https://github.com/andrewdonkin), [Mike Margrain](https://www.linkedin.com/in/mike-margrain-b914381a/), [Nathan Matera](https://www.linkedin.com/in/nathan-matera-0a30b6240/) from the Gallagher team for all their support.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/anomaly/gallagher",
    "name": "gallagher",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.11",
    "maintainer_email": null,
    "keywords": "gallagher, rest, api, tui, cli",
    "author": "Dev Mukherjee",
    "author_email": "devraj@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/bb/aa/5f7d658b5029827659b07987650ec93d34dc31120089ffedca3c267b19de/gallagher-0.1.0a5.tar.gz",
    "platform": null,
    "description": "# Gallagher Python Toolkit\n\n> The missing developer toolkit for Gallagher Command Centre\n\n[![PyPI version](https://badge.fury.io/py/gallagher.svg)](https://badge.fury.io/py/gallagher)\n[![Python Version](https://img.shields.io/pypi/pyversions/gallagher)](https://pypi.org/project/gallagher/)\n[![Build Status](https://github.com/anomaly/gallagher/actions/workflows/run-tests.yml/badge.svg?branch=master)](https://github.com/anomaly/gallagher/actions?query=branch%3Amaster)\n\n<div align=\"center\">\n<img src=\"https://raw.githubusercontent.com/anomaly/gallagher/master/assets/logo-gpt.png\" alt=\"Gallagher Python Toolkit Logo\" height=128 width=128/>\n</div>\n\nGallagher Security manufacture a variety of perimeter [security products](https://security.gallagher.com). At the hear of these is the [Command Centre](https://products.security.gallagher.com/security/au/en_AU/products/software/command-centre/p/C201311) software. Command Centre is deployed locally (in simplistic terms, the complexity varies for every use case). Version `8.6` introduced a REST API which allows you to interact with the system via HTTP requests locally or via Gallagher's [Cloud API Gateway](https://gallaghersecurity.github.io/docs/Command%20Centre%20Cloud%20Api%20Gateway%20TIP.pdf) which eliminates the need for maintaining proxies and VPNs.\n\nOur Python Toolkit focuses on enhancing the developer experience (DX) around the REST API. In principle we provide the following:\n\n- **Python SDK** an idiomatic client (including `asyncio` support) to extend the CC functionality.\n- **Command Line Interface** (CLI) to build powerful pipeline-based workflows.\n- **Terminal User Interface** (TUI) for easy interactions with the Command Centre.\n- **SQL interface** query the REST API as if it were a database or interact with via an ORM.\n\n> [!NOTE]\\\n> This project is **NOT** affiliated with Gallagher Security. All trademarks are the property of their respective owners.\n\nWhile Gallagher maintain a set of [Swagger definitions](https://github.com/gallaghersecurity/cc-rest-docs) for their API, they are primarily intended to generate the documentation [published on Github](https://gallaghersecurity.github.io/cc-rest-docs/ref/index.html). They use a tool called [Spectacle](https://github.com/sourcey/spectacle). Gallagher explicitly state that the Swagger definitions are not intended to be used to generate code. Due to this the API client is hand built and not auto-generated.\n\n> [!IMPORTANT]\\\n> Due to custom annotations the YAML files will not parse with any standard parser.\n\nEverything this project provides hinges upon our Python SDK, designed to enhance the developer experience. It's design is highly opinionated from our experience in building APIs, we ensure conformance with Gallagher software design interfaces.\n\n> [!TIP]\\\n> If you've worked with [stripe-python](https://github.com/stripe/stripe-python) the syntax may feel familiar.\n\nIf you are using one of our user facing tools, it's not important for you to understand how the SDK works, however since it underpins everything, here's a rather sample example:\n\n```python\n# Import core python libs\nimport os\nimport asyncio\n\n# Import the client and models\nfrom gallagher import (\n    cc,\n)\nfrom gallagher.dto.summary import (\n    CardholderSummary,\n)\nfrom gallagher.cc.cardholders import (\n    Cardholder,\n)\n\n# Set the API key from the environment\napi_key = os.environ.get(\"GACC_API_KEY\")\ncc.api_key = api_key\n\n# Async support gives us back a coroutine\nch_coro = Cardholder.list()\n\n# Run the coroutine to get the cardholder\ncardholders = asyncio.run(ch_coro)\ncardholder = cardholders.results[0]\n\n# This is now a pydantic object\ntype(cardholder) == CardholderSummary\n\n# Print out some details from the object\ncardholder.href\ncardholder.first_name\n```\n\n> [!IMPORTANT]\\\n> Gallagher infrastructure deals with perimeter security. We take this extremely seriously and providing a complete test suite to provide that our software meets all standards. These tests constantly run against our _demo_ command centre hosted on the cloud.\n\nThe rest of the README touches upon each of the tools we provide. If you like what you see so far we recommend you [head over to our documentation](https://anomaly.github.io/gallagher).\n\n## Using the CLI and TUI\n\nOur CLI is designed to automate custom workflows via scripts. Inspired by the greatest Unix tools out there, it does one thing and it does it well, leaving you to integrate it into a pipeline. The utility is able to speaking machine readable formats like JSON, YAML and CSV as well as producing formatted output.\n\nHere's an example of fetching the details of a `cardholder`:\n\n```\n(gallagher-py3.11) \u279c  gallagher git:(alpha-3) gala ch get 8272\n person\n                  id 8272\n          first_name Jerry\n           last_name Zurcher\n          short_name None\n         description None\n          authorised yes\n\n  disable_cipher_pad no\n            division 2\n hrefs\n                edit edit\n```\n\n## Interacting via SQL\n\n[Shillelagh](https://shillelagh.readthedocs.io/en/latest/) is a Python library that allows you to interact with REST APIs as if they were SQL databases, including the ability to provide a SQLAlchemy `dialect` allowing you to treat endpoints as a virtual table.\n\nAssuming you had the SQL extensions installed, a simplistic example of querying Cardholders from the command would look like this:\n\n```sql\n\ud83c\udf40> SELECT * FROM \"https://commandcentre-api-au.security.gallagher.cloud/api/cardholders\" WHERE id=8427;\n```\n\nwhich would return a result set of:\n\n```\nfirst_name    last_name    authorised    id\n------------  -----------  ------------ ----\nCammy         Albares      True         8427\n(1 row in 0.23s)\n\n```\n\n## Command Centre API Notes\n\nThe Gallagher API the principles of [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) which ensures that the API is self-descriptive and future proof.\n\nA `href` attribute provides a the destination of referenced objects in the responses. These are full qualified and will be prefixed with the server origin i.e if you are using the Cloud Gateway then all your URLs will be prefixed with the appropriate gateway's address.\n\nThese appear in various forms, starting from as simple as the `href` itself:\n\n```json\n\"cardholders\": {\n    \"href\": \"https://localhost:8904/api/access_groups/352/cardholders\"\n}\n```\n\nthrough to self recursive references (where the data is nested) with additional attributes:\n\n```json\n\"parent\": {\n    \"href\": \"https://localhost:8904/api/access_groups/100\",\n    \"name\": \"All R&D\"\n}\n```\n\n> [!CAUTION]\\\n> Following the design patterns outlined by HATEOAS, you must never hardcode any URLs. You should hit the base API URL which returns the `hrefs` of all other resources.\n> If you are using the Python SDK, then you don't have to worry about this, the client will handle this for you.\n\n## Python SDK Design\n\nThis API client primarily depends on the following libraries:\n\n- [httpx](https://www.python-httpx.org), fo transporting and parsing HTTP requests\n- [pydantic](https://pydantic.dev), for validating responses and constructing request bodies\n\nWe use [Taskfile](https://taskfile.dev) to automate running tasks.\n\nThe project provides a comprehensive set of tests which can be run with `task test`. These tests do create objects in the Command Centre, we advice you to obtain a test license.\n\n> [!IMPORTANT]\n> It's **not recommended** to run tests against a production system.\n\n### Data Transfer Objects\n\nThere are three types of schema definitions, each one of them suffixed with their intent:\n\n- **Ref** are `References` to other objects, they using contain a `href` and possibly additional meta data such as a `name` or `id`\n- **Summary** is what is returned by the Gallagher API in operations such as [searches](https://gallaghersecurity.github.io/cc-rest-docs/ref/cardholders.html), these are generally a subset of the full object\n- **Detail** are the full object found at a particular `href`, they compound on the `Summary` schema and add additional attributes\n- **Response** is a collection of `Summary` objects with other paths like `next` and `previous` for pagination and `updates` for polling results\n- **Payload** is used to send a request to the API\n\nIn summary the properties of each are as follows:\n\n- `Refs` are the minimal pathway to an object\n- `Summary` builds on a `Ref` and provides a subset of the attributes\n- `Detail` builds on a `Summary` and provides the full set of attributes\n- `Response` encapsulates a collection of `Summary` objects, they typically have `next` and `previous` paths for pagination\n- `Payload` are verbose and match the schema definition on the documentation\n\nEach `resource` endpoint subclasses the `APIEndpoint` which marks a resource as `fetchable`, `queryable`, `creatable`, `updatable` and `deletable`. This is determined by the configuration defined using an `EndpointConfig` class.\n\n> [!TIP]\n> The above is meant to be a summary, please see [our documentation](https://anomaly.github.io/gallagher) for more details.\n\n### Schema Design Patterns\n\nOur `schemas` provide a set of `Mixins` that are used to construct the Models. These are repeatable patterns that need not be repeated. The typical patter would be to subclass from the `Mixins` e.g:\n\n```python\nfrom .utils import AppBaseModel, IdentityMixin, HrefMixin\n\nclass AccessGroupRef(\n    AppBaseModel,\n    HrefMixin\n):\n    \"\"\" Access Groups is what a user is assigned to to provide access to doors\n    \"\"\"\n    name: str\n```\n\nwhere the `HrefMixin` (see also `OptionalHrefMixin` for use where the `href` is not always present) provides the `href` attribute:\n\n```python\nclass HrefMixin(BaseModel):\n    \"\"\" Href\n\n    This mixin is used to define the href field for all\n    responses from the Gallagher API.\n    \"\"\"\n    href: str\n```\n\nThese `Mixin` classes can also be used to declare attributes that seek to use the same pattern:\n\n````python\nclass DivisionDetail(\n    AppBaseModel,\n    IdentityMixin,\n):\n    \"\"\" Defines a Division on the Gallagher Command Centre\n    \"\"\"\n\n    name: str\n    description: Optional[str] = None\n    server_display_name: Optional[str] = None\n    parent: OptionalHrefMixin = None\n\n### Schemas\n\nOur `schemas` provide a set of `Mixins` that are used to construct the Models. These are repeatable patterns that need not be repeated. The typical patter would be to subclass from the `Mixins` e.g:\n\n```python\nfrom .utils import AppBaseModel, IdentityMixin, HrefMixin\n\nclass AccessGroupRef(\n    AppBaseModel,\n    HrefMixin\n):\n    \"\"\" Access Groups is what a user is assigned to to provide access to doors\n    \"\"\"\n    name: str\n````\n\nwhere the `HrefMixin` provides the `href` attribute:\n\n```python\nclass HrefMixin(BaseModel):\n    \"\"\" Href\n\n    This mixin is used to define the href field for all\n    responses from the Gallagher API.\n    \"\"\"\n    href: str\n```\n\nThese `Mixin` classes can also be used to declare attributes that seek to use the same pattern:\n\n```python\nclass DivisionDetail(\n    AppBaseModel,\n    IdentityMixin,\n):\n    \"\"\" Outlines the definition of a Division on the Gallagher Command Centre\n    \"\"\"\n\n    name: str\n    description: Optional[str]\n    server_display_name: str\n    parent: Optional[HrefMixin]\n```\n\nwhere `parent` is simply an `href` without any other attributes. In the cases where these attributes have more than just an `href` we defined `Reference` classes:\n\n```python\nclass AccessGroupRef(\n    AppBaseModel,\n    HrefMixin\n):\n    \"\"\" Access Groups is what a user is assigned to to provide access to doors\n    \"\"\"\n    name: str\n```\n\nand use them to populate the attributes:\n\n```python\nclass VisitorTypeDetail(\n    AppBaseModel,\n    IdentityMixin\n):\n    \"\"\"\n    \"\"\"\n    access_group : AccessGroupRef\n    host_access_groups: list[AccessGroupSummary]\n    visitor_access_groups: list[AccessGroupSummary]\n```\n\nIn this example the `AppGroupRef` has a `name` attribute which is not present in the `HrefMixin` class.\n\n> Please see the schema section for naming conventions for `schema` classes\n\nwhere `parent` is simply an `href` without any other attributes. In the cases where these attributes have more than just an `href` we defined `Reference` classes:\n\n```python\nclass AccessGroupRef(\n    AppBaseModel,\n    HrefMixin\n):\n    \"\"\" Access Groups is what a user is assigned to to provide access to doors\n    \"\"\"\n    name: str\n```\n\nand use them to populate the attributes:\n\n```python\nclass VisitorTypeDetail(\n    AppBaseModel,\n    IdentityMixin\n):\n    \"\"\"\n    \"\"\"\n    access_group : AccessGroupRef\n    host_access_groups: list[AccessGroupSummary]\n    visitor_access_groups: list[AccessGroupSummary]\n```\n\nIn this example the `AppGroupRef` has a `name` attribute which is not present in the `HrefMixin` class.\n\n> Please see the schema section for naming conventions for `schema` classes\n\n## Resources\n\nThe following are resources that were discoverd during the design and development of these tools. Not all of them are in use by the toolkit, they were discovered as the library evolved.\n\n### Python Libraries\n\n> [!TIP]\n> Following are Python libraries that I have found during the development of the Gallagher tools. They are not necessarily in use at the moment but a reference in case we need the functionality.\n\n- [plotext](https://github.com/piccolomo/plotext?tab=readme-ov-file) - plots directly on your terminal (something I found when I was exploring apps like [dolphie](https://github.com/charles-001/dolphie))\n- [rich-pixels](https://github.com/darrenburns/rich-pixels) - a [Rich-compatible](https://github.com/Textualize/rich) library for writing pixel images and other colourful grids to the terminal by @darrenburns\n- [PyFilesystem](https://github.com/pyfilesystem/pyfilesystem2) - a Python file system abstraction layer\n\n### Articles\n\n- [A year of building for the terminal](https://textual.textualize.io/blog/2022/12/20/a-year-of-building-for-the-terminal/) by [@darrenburns](https://github.com/darrenburns)\n\n## License\n\nDistributed under the MIT License except Artwork and Branding assets.\n\n## Credits\n\n- [Matthew Skiles](https://matthewskiles.com) for the beautiful logo for the project.\n- [Orion Edwards](https://github.com/borland) for all his support on getting @devraj started with the Gallagher API.\n- [Mick Lambert](https://www.linkedin.com/in/michael-lambert-au/), [Tim Harris](https://www.linkedin.com/in/timharris01/), [Andrew Donkin](https://github.com/andrewdonkin), [Mike Margrain](https://www.linkedin.com/in/mike-margrain-b914381a/), [Nathan Matera](https://www.linkedin.com/in/nathan-matera-0a30b6240/) from the Gallagher team for all their support.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "The missing developer toolkit for Gallagher Command Centre",
    "version": "0.1.0a5",
    "project_urls": {
        "Bug Tracker": "https://github.com/anomaly/gallagher/issues",
        "Documentation": "https://anomaly.github.io/gallagher/",
        "Homepage": "https://github.com/anomaly/gallagher",
        "Repository": "https://github.com/anomaly/gallagher"
    },
    "split_keywords": [
        "gallagher",
        " rest",
        " api",
        " tui",
        " cli"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "76ce79a8cd97aebef8c7bb6d874d0f0497e1007a59a789c1cd6838bc13f7e41f",
                "md5": "d482bf0d09ef514578fb86c9a6ab02a2",
                "sha256": "11c173cb16f2acf33580906fdba6d77cd8073c4eba41066f486d0c7ed6b7eec4"
            },
            "downloads": -1,
            "filename": "gallagher-0.1.0a5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d482bf0d09ef514578fb86c9a6ab02a2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.11",
            "size": 77078,
            "upload_time": "2024-08-25T22:52:49",
            "upload_time_iso_8601": "2024-08-25T22:52:49.471929Z",
            "url": "https://files.pythonhosted.org/packages/76/ce/79a8cd97aebef8c7bb6d874d0f0497e1007a59a789c1cd6838bc13f7e41f/gallagher-0.1.0a5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "bbaa5f7d658b5029827659b07987650ec93d34dc31120089ffedca3c267b19de",
                "md5": "8929e71a9dc74e328c6795b3d495e97d",
                "sha256": "9e1d1aadf532109075c56ddf5a4d1b9baed964de5657765b464ef51ce80c2893"
            },
            "downloads": -1,
            "filename": "gallagher-0.1.0a5.tar.gz",
            "has_sig": false,
            "md5_digest": "8929e71a9dc74e328c6795b3d495e97d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.11",
            "size": 48400,
            "upload_time": "2024-08-25T22:52:50",
            "upload_time_iso_8601": "2024-08-25T22:52:50.759284Z",
            "url": "https://files.pythonhosted.org/packages/bb/aa/5f7d658b5029827659b07987650ec93d34dc31120089ffedca3c267b19de/gallagher-0.1.0a5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-25 22:52:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "anomaly",
    "github_project": "gallagher",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "gallagher"
}
        
Elapsed time: 0.59094s