Skip to content

Commit 9eebb88

Browse files
authored
Merge pull request #88 from anomaly/alpha-15
Alpha 15
2 parents cd720f7 + a096410 commit 9eebb88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+3095
-553
lines changed

Taskfile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ tasks:
6363
dev:docs:
6464
desc: run the mkdocs server with appropriate flags
6565
cmds:
66-
- cd docs && uv run mkdocs serve --open -a localhost:8001
66+
- cd docs && uv run -m mkdocs serve --open -a localhost:8001
6767
debug:get:
6868
desc: use httpie to get payload from CC
6969
summary: |

docs/docs/cli.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ We follow a `git` like `command`, `sub-command` pattern, so it should feel quite
88

99
uv will install the alias `gal` for you to interact with the CLI. You can ask for help with:
1010

11-
::: mkdocs-typer
12-
:module: gallagher.cli
13-
:command: app
11+
::: mkdocs-typer2
12+
:module: gallagher.cli
13+
:name: app
1414

1515
## Resources
1616

docs/docs/design.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ There are three types of schema definitions, each one of them suffixed with thei
5555
- **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
5656
- **Detail** are the full object found at a particular `href`, they compound on the `Summary` schema and add additional attributes
5757
- **Responses** are resposnes sent back from the server, these will typically contain a set of `Summary` or `Detail` objects. When fetching _detailed_ responses for an object the server will often respond with a `Detail` object without a wrapper `Response` object.
58+
- **Payloads** are objects that are sent to the server as part of a `POST` or `PUT` operation, these are suffixed with **Payload**, some of these also offer `Builder` classes to assist with construction of the payload.
5859

5960
I additional we have classes that defined responses which are suffixed with **Response**, these wrap structures which returns `hrefs` for `next` and `previous` responses and usually have a collection to hold the response.
6061

docs/mkdocs.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@ theme:
1010
name: material
1111

1212
markdown_extensions:
13-
- mkdocs-typer
14-
- codehilite
15-
- pymdownx.highlight:
16-
anchor_linenums: true
17-
line_spans: __span
18-
pygments_lang_class: true
19-
- pymdownx.inlinehilite
20-
- pymdownx.snippets
21-
- pymdownx.superfences
22-
- admonition
13+
- codehilite
14+
- pymdownx.highlight:
15+
anchor_linenums: true
16+
line_spans: __span
17+
pygments_lang_class: true
18+
- pymdownx.inlinehilite
19+
- pymdownx.snippets
20+
- pymdownx.superfences
21+
- admonition
2322

24-
plugins:
23+
plugins:
2524
- social
2625
- search
2726
- mkdocstrings
27+
- mkdocs-typer2
2828

2929
nav:
3030
- Introduction: index.md
3131
- Installation & Usage: installation.md
3232
- Python SDK: python-sdk.md
3333
- SQL and SQLAlchemy: sql.md
3434
- Command Line Interface: cli.md
35-
- Terminal User Interface: tui.md
35+
- Terminal User Interface: tui.md
3636
- Contributor's Manual: design.md

gallagher/cc/access_groups.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
""" Access Groups
2+
3+
"""
4+
5+
from gallagher.cc.core import Capabilities, APIEndpoint, EndpointConfig
6+
7+
from ..dto.detail import AccessGroupDetail
8+
from ..dto.response import AccessGroupResponse
9+
10+
11+
class AccessGroups(APIEndpoint):
12+
"""Access Groups
13+
14+
Provides access to access group operations including listing,
15+
retrieving, creating, and updating access groups.
16+
"""
17+
18+
@classmethod
19+
async def get_config(cls) -> EndpointConfig:
20+
return EndpointConfig(
21+
endpoint=Capabilities.CURRENT.features.access_groups.access_groups,
22+
dto_list=AccessGroupResponse,
23+
dto_retrieve=AccessGroupDetail,
24+
)

gallagher/cc/cardholders/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
33
"""
44

5-
from ..core import Capabilities, APIEndpoint, EndpointConfig
5+
from ..core import (
6+
Capabilities,
7+
APIEndpoint,
8+
EndpointConfig,
9+
)
610

711
from ...dto.detail import (
812
CardholderDetail,

gallagher/cc/core.py

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
NoAPIKeyProvidedError,
6161
)
6262

63+
from ..dto.detail.discover import FeaturesDetail
64+
6365

6466
def _check_api_key_format(api_key):
6567
"""Validates that the Gallagher Key is in the right format.
@@ -88,6 +90,7 @@ def _sanitise_name_param(name: str) -> str:
8890

8991
return f"%{name}%"
9092

93+
9194
def _get_authorization_headers():
9295
"""Creates an authorization header for Gallagher API calls
9396
@@ -115,7 +118,7 @@ def _get_authorization_headers():
115118
client properly.
116119
"""
117120
raise NoAPIKeyProvidedError()
118-
121+
119122
if not _check_api_key_format(api_key):
120123
""" API key is not in the right format
121124
@@ -162,7 +165,7 @@ class EndpointConfig:
162165
sort: str = "id" # Can be set to id or -id
163166

164167
fields: Tuple[str] = () # Optional list of fields, blank = all
165-
search: Tuple[str] = () # If the endpoint supports search, blank = none
168+
search: Tuple[str] = () # If the endpoint supports search, blank = none
166169

167170
@classmethod
168171
async def validate_endpoint(cls):
@@ -180,21 +183,22 @@ class Capabilities:
180183
Discover response object, each endpoint will reference
181184
one of the instance variable Href property to get the
182185
path to the endpoint.
183-
186+
184187
Gallagher recommends that the endpoints not be hardcoded
185188
into the client and instead be discovered at runtime.
186-
189+
187190
Note that if a feature has not been licensed by a client
188191
then the path will be set to None, if the client attempts
189192
to access the endpoint then the library will throw an exception
190-
193+
191194
This value is memoized and should be performant
192195
"""
193196
CURRENT = DiscoveryResponse(
194197
version="0.0.0.0", # Indicates that it's not been discovered
195198
features=FeaturesDetail(),
196199
)
197200

201+
198202
class APIEndpoint:
199203
"""Base class for all API objects
200204
@@ -233,7 +237,7 @@ async def get_config(cls) -> EndpointConfig:
233237
provide additional configuration options.
234238
"""
235239
raise NotImplementedError("get_config method not implemented")
236-
240+
237241
@classmethod
238242
def _ssl_context(cls):
239243
"""Returns the SSL context for the endpoint
@@ -246,7 +250,7 @@ def _ssl_context(cls):
246250
if not file_tls_certificate:
247251
"""TLS certificate is required for SSL context"""
248252
return None
249-
253+
250254
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
251255
context.load_cert_chain(file_tls_certificate, file_private_key)
252256
return context
@@ -288,9 +292,9 @@ async def _discover(cls):
288292
from . import api_base
289293

290294
async with httpx.AsyncClient(
291-
proxy=proxy_address,
292-
verify=cls._ssl_context(),
293-
) as _httpx_async:
295+
proxy=proxy_address,
296+
verify=cls._ssl_context(),
297+
) as _httpx_async:
294298
# Don't use the _get wrapper here, we need to get the raw response
295299
response = await _httpx_async.get(
296300
api_base,
@@ -320,7 +324,6 @@ async def _discover(cls):
320324
# copied not referenced.
321325
cls.__config__ = await cls.get_config()
322326

323-
324327
@classmethod
325328
async def list(cls, skip=0):
326329
"""For a list of objects for the given resource
@@ -374,9 +377,9 @@ async def search(
374377
cls,
375378
sort: SearchSortOrder = SearchSortOrder.ID,
376379
top: int = 100,
377-
name: Optional[str] = None, # TODO: en
378-
division: str = None, # TODO: use division type
379-
direct_division: str = None, # TODO: use division type
380+
name: Optional[str] = None, # TODO: en
381+
division: str = None, # TODO: use division type
382+
direct_division: str = None, # TODO: use division type
380383
description: Optional[str] = None,
381384
fields: str | List[str] = "defaults",
382385
**kwargs,
@@ -429,7 +432,6 @@ async def search(
429432
params=params,
430433
)
431434

432-
433435
# Follow links methods, these are valid based on if the response
434436
# classes make available a next, previous or update href, otherwise
435437
# the client raises an NotImplementedError
@@ -452,7 +454,8 @@ async def next(cls, response):
452454

453455
if not response.next:
454456
"""We have no where to go based on the passed response"""
455-
raise DeadEndException("No further paths to follow for this endpoint")
457+
raise DeadEndException(
458+
"No further paths to follow for this endpoint")
456459

457460
return await cls._get(
458461
response.next.href,
@@ -486,7 +489,7 @@ async def previous(cls, response):
486489
@classmethod
487490
async def follow(
488491
cls,
489-
asyncio_event: AsyncioEvent, # Not to be confused with Gallagher event
492+
asyncio_event: AsyncioEvent, # Not to be confused with Gallagher event
490493
params: dict[str, Any] = {},
491494
):
492495
"""Fetches update and follows next to get the next set of results
@@ -516,9 +519,9 @@ async def follow(
516519
url = f"{cls.__config__.endpoint_follow.href}"
517520

518521
async with httpx.AsyncClient(
519-
proxy=proxy_address,
520-
verify=cls._ssl_context(),
521-
) as _httpx_async:
522+
proxy=proxy_address,
523+
verify=cls._ssl_context(),
524+
) as _httpx_async:
522525

523526
while not asyncio_event.is_set():
524527
try:
@@ -576,9 +579,9 @@ async def _get(
576579
:param AppBaseModel response_class: DTO to be used for list requests
577580
"""
578581
async with httpx.AsyncClient(
579-
proxy=proxy_address,
580-
verify=cls._ssl_context(),
581-
) as _httpx_async:
582+
proxy=proxy_address,
583+
verify=cls._ssl_context(),
584+
) as _httpx_async:
582585

583586
try:
584587

@@ -627,9 +630,9 @@ async def _post(
627630
parsing and sending out a body as part of the request.
628631
"""
629632
async with httpx.AsyncClient(
630-
proxy=proxy_address,
631-
verify=cls._ssl_context(),
632-
) as _httpx_async:
633+
proxy=proxy_address,
634+
verify=cls._ssl_context(),
635+
) as _httpx_async:
633636

634637
try:
635638

gallagher/cc/doors.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from gallagher.cc.core import Capabilities, APIEndpoint, EndpointConfig
2+
3+
from ..dto.detail import DoorDetail
4+
from ..dto.response import DoorResponse
5+
6+
7+
class Doors(APIEndpoint):
8+
"""Doors
9+
10+
Provides access to door operations including listing,
11+
retrieving, creating, and updating doors.
12+
"""
13+
14+
@classmethod
15+
async def get_config(cls) -> EndpointConfig:
16+
return EndpointConfig(
17+
endpoint=Capabilities.CURRENT.features.doors.doors,
18+
dto_list=DoorResponse,
19+
dto_retrieve=DoorDetail,
20+
)

gallagher/cc/lockers.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
""" Lockers
2+
3+
"""
4+
5+
from gallagher.cc.core import Capabilities, APIEndpoint, EndpointConfig
6+
7+
from ..dto.detail import LockerDetail
8+
from ..dto.response import LockerResponse
9+
10+
11+
class Lockers(APIEndpoint):
12+
"""Lockers
13+
14+
Provides access to locker operations including listing,
15+
retrieving, creating, and updating lockers.
16+
"""
17+
18+
@classmethod
19+
async def get_config(cls) -> EndpointConfig:
20+
return EndpointConfig(
21+
endpoint=Capabilities.CURRENT.features.locker_banks.locker_banks,
22+
dto_list=LockerResponse,
23+
dto_retrieve=LockerDetail,
24+
)

gallagher/cc/operators.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
""" Operators
2+
3+
"""
4+
5+
from gallagher.cc.core import Capabilities, APIEndpoint, EndpointConfig
6+
7+
from ..dto.detail import OperatorDetail
8+
from ..dto.response import OperatorResponse
9+
10+
11+
class Operators(APIEndpoint):
12+
"""Operators
13+
14+
Provides access to operator operations including listing,
15+
retrieving, creating, and updating operators.
16+
"""
17+
18+
@classmethod
19+
async def get_config(cls) -> EndpointConfig:
20+
return EndpointConfig(
21+
endpoint=Capabilities.CURRENT.features.operator_groups.operator_groups,
22+
dto_list=OperatorResponse,
23+
dto_retrieve=OperatorDetail,
24+
)

0 commit comments

Comments
 (0)