Skip to content

Commit

Permalink
#8 - add schema-level and service-level handling of multiple content …
Browse files Browse the repository at this point in the history
…types

still need to add protocol-level and message-level handling

Signed-off-by: Lance Drane <[email protected]>
  • Loading branch information
Lance-Drane committed Jul 30, 2024
1 parent ef2b56e commit dcd536e
Show file tree
Hide file tree
Showing 30 changed files with 324 additions and 248 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.2
rev: v0.5.5
hooks:
- id: ruff
args: [ --fix ]
Expand Down
4 changes: 2 additions & 2 deletions examples/1_hello_world/hello_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand All @@ -13,7 +13,7 @@


def simple_client_callback(
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_JSON_VALUE
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_RESPONSE_VALUE
) -> None:
"""This simply prints the response from the service to your console.
Expand Down
4 changes: 2 additions & 2 deletions examples/1_hello_world_amqp/hello_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand All @@ -14,7 +14,7 @@


def simple_client_callback(
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_JSON_VALUE
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_RESPONSE_VALUE
) -> None:
"""This simply prints the response from the service to your console.
Expand Down
6 changes: 3 additions & 3 deletions examples/1_hello_world_events/hello_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand All @@ -13,7 +13,7 @@


def simple_client_callback(
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_JSON_VALUE
_source: str, _operation: str, _has_error: bool, payload: INTERSECT_RESPONSE_VALUE
) -> None:
"""This simply prints the response from the service to your console.
Expand All @@ -37,7 +37,7 @@ def simple_client_callback(


def simple_event_callback(
_source: str, _operation: str, _event_name: str, payload: INTERSECT_JSON_VALUE
_source: str, _operation: str, _event_name: str, payload: INTERSECT_RESPONSE_VALUE
) -> None:
"""This simply prints the event from the service to your console.
Expand Down
4 changes: 2 additions & 2 deletions examples/2_counting/counting_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import time

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand Down Expand Up @@ -95,7 +95,7 @@ def __init__(self) -> None:
self.message_stack.reverse()

def client_callback(
self, source: str, operation: str, _has_error: bool, payload: INTERSECT_JSON_VALUE
self, source: str, operation: str, _has_error: bool, payload: INTERSECT_RESPONSE_VALUE
) -> IntersectClientCallback:
"""This simply prints the response from the Service to your console.
Expand Down
4 changes: 2 additions & 2 deletions examples/2_counting_events/counting_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand All @@ -26,7 +26,7 @@ def __init__(self) -> None:
self.events_encountered = 0

def event_callback(
self, _source: str, _operation: str, _event_name: str, payload: INTERSECT_JSON_VALUE
self, _source: str, _operation: str, _event_name: str, payload: INTERSECT_RESPONSE_VALUE
) -> None:
"""Handles events from the Counting Service.
Expand Down
4 changes: 2 additions & 2 deletions examples/3_ping_pong_events/ping_pong_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand All @@ -27,7 +27,7 @@ def __init__(self) -> None:
self.events_encountered = 0

def event_callback(
self, _source: str, _operation: str, event_name: str, payload: INTERSECT_JSON_VALUE
self, _source: str, _operation: str, event_name: str, payload: INTERSECT_RESPONSE_VALUE
) -> IntersectClientCallback:
"""Handles events from two Services at once.
Expand Down
4 changes: 2 additions & 2 deletions examples/3_ping_pong_events_amqp/ping_pong_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from intersect_sdk import (
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClient,
IntersectClientCallback,
IntersectClientConfig,
Expand Down Expand Up @@ -30,7 +30,7 @@ def __init__(self) -> None:
self.events_encountered = 0

def event_callback(
self, _source: str, _operation: str, event_name: str, payload: INTERSECT_JSON_VALUE
self, _source: str, _operation: str, event_name: str, payload: INTERSECT_RESPONSE_VALUE
) -> IntersectClientCallback:
"""Handles events from two Services at once.
Expand Down
37 changes: 19 additions & 18 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ isort = { known-first-party = ['src'] }
pydocstyle = { convention = 'google'}
flake8-quotes = {inline-quotes = 'single', multiline-quotes = 'double'}
mccabe = { max-complexity = 20 }
pylint = { max-args = 10, max-branches = 20, max-returns = 10 }
pylint = { max-args = 10, max-branches = 20, max-returns = 10, max-statements = 75 }
# pyflakes and the relevant pycodestyle rules are already configured
extend-select = [
'C90', # mccabe complexity
Expand Down
2 changes: 2 additions & 0 deletions src/intersect_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
INTERSECT_CLIENT_EVENT_CALLBACK_TYPE,
INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE,
INTERSECT_JSON_VALUE,
INTERSECT_RESPONSE_VALUE,
IntersectClientCallback,
IntersectClientMessageParams,
)
Expand Down Expand Up @@ -51,6 +52,7 @@
'INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE',
'INTERSECT_CLIENT_EVENT_CALLBACK_TYPE',
'INTERSECT_JSON_VALUE',
'INTERSECT_RESPONSE_VALUE',
'IntersectBaseCapabilityImplementation',
'default_intersect_lifecycle_loop',
'IntersectClientConfig',
Expand Down
4 changes: 2 additions & 2 deletions src/intersect_sdk/_internal/data_plane/minio_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def send_minio_object(
"""
bucket_name = _condense_minio_bucket_name(hierarchy)
# mimetypes.guess_extension() is a nice-to-have for MINIO preview, but isn't essential.
object_id = str(uuid4()) + (mimetypes.guess_extension(content_type.value) or '')
object_id = str(uuid4()) + (mimetypes.guess_extension(content_type) or '')
try:
if not provider.bucket_exists(bucket_name):
provider.make_bucket(bucket_name)
Expand All @@ -96,7 +96,7 @@ def send_minio_object(
object_name=object_id,
data=buff_data,
length=buff_data.getbuffer().nbytes,
content_type=content_type.value,
content_type=content_type,
)
return MinioPayload(
minio_url=provider._base_url._url.geturl(), # noqa: SLF001 (only way to get URL from MINIO API)
Expand Down
4 changes: 2 additions & 2 deletions src/intersect_sdk/_internal/event_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def definition_metadata_differences(
differences.append(
(
'content_type',
f'{definition.content_type.__class__.__name__}.{definition.content_type.name}',
f'{metadata.content_type.__class__.__name__}.{metadata.content_type.name}',
f'{definition.content_type}',
f'{metadata.content_type}',
)
)
if definition.data_handler != metadata.data_transfer_handler:
Expand Down
37 changes: 29 additions & 8 deletions src/intersect_sdk/_internal/function_metadata.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,48 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Callable, NamedTuple
from typing import TYPE_CHECKING, Any, Literal, NamedTuple

if TYPE_CHECKING:
from pydantic import TypeAdapter

from ..core_definitions import IntersectDataHandler, IntersectMimeType


class FunctionMetadata(NamedTuple):
"""Internal cache of public function metadata.
NOTE: both this class and all properties in it should remain immutable after creation
"""

method: Callable[[Any], Any]
request_adapter: TypeAdapter[Any] | Literal[0] | None
"""
Type adapter for serializing and validating requests.
Null if user did not specify a request parameter.
0 if user did specify a request parameter, but Content-Type does not require a TypeAdapter.
"""
The raw method of the function. The function itself is useless and should not be called,
but will store user-defined attributes needed for internal handling of data.
response_adapter: TypeAdapter[Any] | Literal[0]
"""
request_adapter: TypeAdapter[Any] | None
Type adapter for serializing and validating responses.
0 if Content-Type does not require a TypeAdapter.
"""
Type adapter for serializing and validating requests. Should only be null if user did not specify a request parameter.
request_content_type: IntersectMimeType
"""
response_adapter: TypeAdapter[Any]
Content-Type of the request value
"""
Type adapter for serializing and validating responses.
response_content_type: IntersectMimeType
"""
Content-Type of the response value
"""
response_data_transfer_handler: IntersectDataHandler
"""
How we intend on handling the response value
"""
strict_validation: bool
"""
Whether or not we're using lenient Pydantic validation (default, False) or strict
"""
shutdown_keys: set[str]
"""
keys which should cause the function to be skipped if set
"""
2 changes: 1 addition & 1 deletion src/intersect_sdk/_internal/messages/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class EventMessage(TypedDict):
NOTE: The payload's contents will differ based on the data_handler property in the message header.
"""

contentType: Annotated[IntersectMimeType, Field(IntersectMimeType.JSON)]
contentType: Annotated[IntersectMimeType, Field('application/json')]
"""
The content type to use when encoding/decoding a message's payload.
The value MUST be a specific media type (e.g. application/json).
Expand Down
2 changes: 1 addition & 1 deletion src/intersect_sdk/_internal/messages/userspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class UserspaceMessage(TypedDict):
NOTE: If "has_error" flag in the message headers is set to "True", the payload will instead contain an error string.
"""

contentType: Annotated[IntersectMimeType, Field(IntersectMimeType.JSON)]
contentType: Annotated[IntersectMimeType, Field('application/json')]
"""
The content type to use when encoding/decoding a message's payload.
The value MUST be a specific media type (e.g. application/json).
Expand Down
Loading

0 comments on commit dcd536e

Please sign in to comment.