try:
async for result in self._documents._stub.query_stream(
- collection=_collection_to_wire(self._collection),
- expressions=self._expressions_to_wire(),
- limit=self._limit,
+ document_query_stream_request=DocumentQueryStreamRequest(
+ collection=_collection_to_wire(self._collection),
+ expressions=self._expressions_to_wire(),
+ limit=self._limit,
+ )
):
yield _document_from_wire(documents=self._documents, message=result.document)
except GRPCError as grpc_err:
@@ -1799,7 +1839,8 @@
Examples
Expand source code
-
class QueryResultsPage:
+
@dataclass(frozen=True, order=True)
+class QueryResultsPage:
"""Represents a page of results from a query."""
paging_token: any = field(default_factory=lambda: None)
diff --git a/docs/nitric/api/events.html b/docs/nitric/api/events.html
index d14ac05..aaca3c8 100644
--- a/docs/nitric/api/events.html
+++ b/docs/nitric/api/events.html
@@ -52,7 +52,13 @@
Module nitric.api.events
from nitric.api.exception import exception_from_grpc_error
from nitric.utils import new_default_channel, _struct_from_dict
-from proto.nitric.event.v1 import EventServiceStub, NitricEvent, TopicServiceStub
+from nitric.proto.nitric.event.v1 import (
+ EventServiceStub,
+ NitricEvent,
+ TopicServiceStub,
+ EventPublishRequest,
+ TopicListRequest,
+)
from dataclasses import dataclass, field
@@ -74,7 +80,7 @@
Module nitric.api.events
@dataclass(frozen=True, order=True)
-class Topic(object):
+class TopicRef(object):
"""A reference to a topic on an event service, used to perform operations on that topic."""
_events: Events
@@ -94,11 +100,12 @@
Module nitric.api.events
event = Event()
if isinstance(event, dict):
- # TODO: handle events that are just a payload
- event = Event(**event)
+ event = Event(payload=event)
try:
- response = await self._events._stub.publish(topic=self.name, event=_event_to_wire(event))
+ response = await self._events._stub.publish(
+ event_publish_request=EventPublishRequest(topic=self.name, event=_event_to_wire(event))
+ )
return Event(**{**event.__dict__.copy(), **{"id": response.id}})
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -122,17 +129,17 @@
Module nitric.api.events
if self.channel is not None:
self.channel.close()
- async def topics(self) -> List[Topic]:
+ async def topics(self) -> List[TopicRef]:
"""Get a list of topics available for publishing or subscription."""
try:
- response = await self._topic_stub.list()
+ response = await self._topic_stub.list(topic_list_request=TopicListRequest())
return [self.topic(topic.name) for topic in response.topics]
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
- def topic(self, name: str) -> Topic:
+ def topic(self, name: str) -> TopicRef:
"""Return a reference to a topic."""
- return Topic(_events=self, name=name)
+ return TopicRef(_events=self, name=name)
@@ -154,7 +161,8 @@
Classes
Expand source code
-
class Event(object):
+
@dataclass(frozen=True, order=True)
+class Event(object):
"""Eventing client, providing access to Topic and Event references and operations on those entities."""
payload: dict = field(default_factory=dict)
@@ -206,22 +214,22 @@
Class variables
if self.channel is not None:
self.channel.close()
- async def topics(self) -> List[Topic]:
+ async def topics(self) -> List[TopicRef]:
"""Get a list of topics available for publishing or subscription."""
try:
- response = await self._topic_stub.list()
+ response = await self._topic_stub.list(topic_list_request=TopicListRequest())
return [self.topic(topic.name) for topic in response.topics]
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
- def topic(self, name: str) -> Topic:
+ def topic(self, name: str) -> TopicRef:
"""Return a reference to a topic."""
- return Topic(_events=self, name=name)
Get a list of topics available for publishing or subscription.
@@ -243,10 +251,10 @@
Methods
Expand source code
-
async def topics(self) -> List[Topic]:
+
async def topics(self) -> List[TopicRef]:
"""Get a list of topics available for publishing or subscription."""
try:
- response = await self._topic_stub.list()
+ response = await self._topic_stub.list(topic_list_request=TopicListRequest())
return [self.topic(topic.name) for topic in response.topics]
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@dataclass(frozen=True, order=True)
+class TopicRef(object):
"""A reference to a topic on an event service, used to perform operations on that topic."""
_events: Events
@@ -284,25 +293,26 @@
Methods
event = Event()
if isinstance(event, dict):
- # TODO: handle events that are just a payload
- event = Event(**event)
+ event = Event(payload=event)
try:
- response = await self._events._stub.publish(topic=self.name, event=_event_to_wire(event))
+ response = await self._events._stub.publish(
+ event_publish_request=EventPublishRequest(topic=self.name, event=_event_to_wire(event))
+ )
return Event(**{**event.__dict__.copy(), **{"id": response.id}})
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@dataclass(frozen=True, order=True)
+class Event(object):
"""Eventing client, providing access to Topic and Event references and operations on those entities."""
payload: dict = field(default_factory=dict)
@@ -219,22 +220,22 @@
Class variables
if self.channel is not None:
self.channel.close()
- async def topics(self) -> List[Topic]:
+ async def topics(self) -> List[TopicRef]:
"""Get a list of topics available for publishing or subscription."""
try:
- response = await self._topic_stub.list()
+ response = await self._topic_stub.list(topic_list_request=TopicListRequest())
return [self.topic(topic.name) for topic in response.topics]
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
- def topic(self, name: str) -> Topic:
+ def topic(self, name: str) -> TopicRef:
"""Return a reference to a topic."""
- return Topic(_events=self, name=name)
Get a list of topics available for publishing or subscription.
@@ -256,10 +257,10 @@
Methods
Expand source code
-
async def topics(self) -> List[Topic]:
+
async def topics(self) -> List[TopicRef]:
"""Get a list of topics available for publishing or subscription."""
try:
- response = await self._topic_stub.list()
+ response = await self._topic_stub.list(topic_list_request=TopicListRequest())
return [self.topic(topic.name) for topic in response.topics]
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
def queue(self, name: str):
"""Return a reference to a queue from the connected queue service."""
- return Queue(_queueing=self, name=name)
+ return QueueRef(_queueing=self, name=name)
Methods
@@ -334,7 +336,7 @@
Methods
def queue(self, name: str):
"""Return a reference to a queue from the connected queue service."""
- return Queue(_queueing=self, name=name)
+ return QueueRef(_queueing=self, name=name)
@@ -369,7 +371,7 @@
Methods
def secret(self, name: str):
"""Return a reference to a secret container from the connected secrets management service."""
- return SecretContainer(_secrets=self, name=name)
def secret(self, name: str):
"""Return a reference to a secret container from the connected secrets management service."""
- return SecretContainer(_secrets=self, name=name)
@dataclass(frozen=True, order=True)
+class TopicRef(object):
"""A reference to a topic on an event service, used to perform operations on that topic."""
_events: Events
@@ -506,25 +510,26 @@
Class variables
event = Event()
if isinstance(event, dict):
- # TODO: handle events that are just a payload
- event = Event(**event)
+ event = Event(payload=event)
try:
- response = await self._events._stub.publish(topic=self.name, event=_event_to_wire(event))
+ response = await self._events._stub.publish(
+ event_publish_request=EventPublishRequest(topic=self.name, event=_event_to_wire(event))
+ )
return Event(**{**event.__dict__.copy(), **{"id": response.id}})
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@dataclass(frozen=True, order=True)
-class Queue(object):
+class QueueRef(object):
"""A reference to a queue from a queue service, used to perform operations on that queue."""
_queueing: Queues
@@ -177,7 +187,9 @@
@dataclass(frozen=True, order=True)
+class QueueRef(object):
"""A reference to a queue from a queue service, used to perform operations on that queue."""
_queueing: Queues
@@ -318,7 +336,9 @@
def queue(self, name: str):
"""Return a reference to a queue from the connected queue service."""
- return Queue(_queueing=self, name=name)
+ return QueueRef(_queueing=self, name=name)
Methods
@@ -496,7 +524,7 @@
Methods
def queue(self, name: str):
"""Return a reference to a queue from the connected queue service."""
- return Queue(_queueing=self, name=name)
+ return QueueRef(_queueing=self, name=name)
@@ -511,7 +539,8 @@
Methods
Expand source code
-
class ReceivedTask(object):
+
@dataclass(frozen=True, order=True)
+class ReceivedTask(object):
"""A reference to a task received from a Queue, with a lease."""
id: str = field(default=None)
@@ -519,7 +548,7 @@
"Task is missing internal client or lease id, was it returned from " "queue.receive?"
)
try:
- await self._queueing._queue_stub.complete(queue=self._queue.name, lease_id=self.lease_id)
+ await self._queueing._queue_stub.complete(
+ queue_complete_request=QueueCompleteRequest(queue=self._queue.name, lease_id=self.lease_id)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -578,7 +609,9 @@
Methods
"Task is missing internal client or lease id, was it returned from " "queue.receive?"
)
try:
- await self._queueing._queue_stub.complete(queue=self._queue.name, lease_id=self.lease_id)
+ await self._queueing._queue_stub.complete(
+ queue_complete_request=QueueCompleteRequest(queue=self._queue.name, lease_id=self.lease_id)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -595,7 +628,8 @@
Methods
Expand source code
-
class Task(object):
+
@dataclass(frozen=True, order=True)
+class Task(object):
"""A task to be sent to a Queue."""
id: str = field(default=None)
@@ -645,11 +679,11 @@
"""A reference to a version of a secret, used to access the value of the version."""
_secrets: Secrets
- secret: SecretContainer
+ secret: SecretContainerRef
id: str
async def access(self) -> SecretValue:
"""Return the value stored against this version of the secret."""
version_message = _secret_version_to_wire(self)
try:
- response = await self._secrets._secrets_stub.access(secret_version=version_message)
+ response = await self._secrets._secrets_stub.access(
+ secret_access_request=SecretAccessRequest(secret_version=version_message)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -187,8 +197,8 @@
@dataclass(frozen=True)
+class SecretContainerRef(object):
"""A reference to a secret container, used to store and retrieve secret versions."""
_secrets: Secrets
@@ -215,7 +226,9 @@
@dataclass(frozen=True)
+class SecretValue(object):
"""Represents the value of a secret, tied to a specific version."""
# The version containing this value. Never 'latest', always a specific version.
@@ -392,7 +408,7 @@
A reference to a version of a secret, used to access the value of the version.
@@ -400,18 +416,21 @@
Methods
Expand source code
-
class SecretVersion(object):
+
@dataclass(frozen=True)
+class SecretVersion(object):
"""A reference to a version of a secret, used to access the value of the version."""
_secrets: Secrets
- secret: SecretContainer
+ secret: SecretContainerRef
id: str
async def access(self) -> SecretValue:
"""Return the value stored against this version of the secret."""
version_message = _secret_version_to_wire(self)
try:
- response = await self._secrets._secrets_stub.access(secret_version=version_message)
+ response = await self._secrets._secrets_stub.access(
+ secret_access_request=SecretAccessRequest(secret_version=version_message)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -432,7 +451,7 @@
"""Return the value stored against this version of the secret."""
version_message = _secret_version_to_wire(self)
try:
- response = await self._secrets._secrets_stub.access(secret_version=version_message)
+ response = await self._secrets._secrets_stub.access(
+ secret_access_request=SecretAccessRequest(secret_version=version_message)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -500,7 +521,7 @@
Methods
def secret(self, name: str):
"""Return a reference to a secret container from the connected secrets management service."""
- return SecretContainer(_secrets=self, name=name)
def secret(self, name: str):
"""Return a reference to a secret container from the connected secrets management service."""
- return SecretContainer(_secrets=self, name=name)
def bucket(self, name: str):
"""Return a reference to a bucket from the connected storage service."""
- return Bucket(_storage=self, name=name)
+ return BucketRef(_storage=self, name=name)
@dataclass(frozen=True, order=True)
-class Bucket(object):
+class BucketRef(object):
"""A reference to a bucket in a storage service, used to the perform operations on that bucket."""
_storage: Storage
@@ -86,6 +94,29 @@
Module nitric.api.storage
"""Return a reference to a file in this bucket."""
return File(_storage=self._storage, _bucket=self.name, key=key)
+ async def files(self):
+ """Return a list of files in this bucket."""
+ resp = await self._storage._storage_stub.list_files(
+ storage_list_files_request=StorageListFilesRequest(bucket_name=self.name)
+ )
+ return [self.file(f.key) for f in resp.files]
+
+
+class FileMode(Enum):
+ """Definition of available operation modes for file signed URLs."""
+
+ READ = 0
+ WRITE = 1
+
+ def to_request_operation(self) -> StoragePreSignUrlRequestOperation:
+ """Convert FileMode to a StoragePreSignUrlRequestOperation."""
+ if self == FileMode.READ:
+ return StoragePreSignUrlRequestOperation.READ
+ elif self == FileMode.WRITE:
+ return StoragePreSignUrlRequestOperation.WRITE
+ else:
+ raise InvalidArgumentException("Invalid FileMode")
+
@dataclass(frozen=True, order=True)
class File(object):
@@ -102,14 +133,18 @@
Module nitric.api.storage
Will create the file if it doesn't already exist.
"""
try:
- await self._storage._storage_stub.write(bucket_name=self._bucket, key=self.key, body=body)
+ await self._storage._storage_stub.write(
+ storage_write_request=StorageWriteRequest(bucket_name=self._bucket, key=self.key, body=body)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
async def read(self) -> bytes:
"""Read this files contents from the bucket."""
try:
- response = await self._storage._storage_stub.read(bucket_name=self._bucket, key=self.key)
+ response = await self._storage._storage_stub.read(
+ storage_read_request=StorageReadRequest(bucket_name=self._bucket, key=self.key)
+ )
return response.body
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -117,7 +152,29 @@
Module nitric.api.storage
async def delete(self):
"""Delete this file from the bucket."""
try:
- await self._storage._storage_stub.delete(bucket_name=self._bucket, key=self.key)
+ await self._storage._storage_stub.delete(
+ storage_delete_request=StorageDeleteRequest(bucket_name=self._bucket, key=self.key)
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ async def upload_url(self, expiry: int = 600):
+ """Get a temporary writable URL to this file."""
+ await self.sign_url(mode=FileMode.WRITE, expiry=expiry)
+
+ async def download_url(self, expiry: int = 600):
+ """Get a temporary readable URL to this file."""
+ await self.sign_url(mode=FileMode.READ, expiry=expiry)
+
+ async def sign_url(self, mode: FileMode = FileMode.READ, expiry: int = 3600):
+ """Generate a signed URL for reading or writing to a file."""
+ try:
+ response = await self._storage._storage_stub.pre_sign_url(
+ storage_pre_sign_url_request=StoragePreSignUrlRequest(
+ bucket_name=self._bucket, key=self.key, operation=mode.to_request_operation(), expiry=expiry
+ )
+ )
+ return response.url
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -131,8 +188,8 @@
@dataclass(frozen=True, order=True)
+class BucketRef(object):
"""A reference to a bucket in a storage service, used to the perform operations on that bucket."""
_storage: Storage
@@ -149,18 +207,25 @@
Classes
def file(self, key: str):
"""Return a reference to a file in this bucket."""
- return File(_storage=self._storage, _bucket=self.name, key=key)
+ return File(_storage=self._storage, _bucket=self.name, key=key)
+
+ async def files(self):
+ """Return a list of files in this bucket."""
+ resp = await self._storage._storage_stub.list_files(
+ storage_list_files_request=StorageListFilesRequest(bucket_name=self.name)
+ )
+ return [self.file(f.key) for f in resp.files]
async def files(self):
+ """Return a list of files in this bucket."""
+ resp = await self._storage._storage_stub.list_files(
+ storage_list_files_request=StorageListFilesRequest(bucket_name=self.name)
+ )
+ return [self.file(f.key) for f in resp.files]
+
+
@@ -186,7 +268,8 @@
Methods
Expand source code
-
class File(object):
+
@dataclass(frozen=True, order=True)
+class File(object):
"""A reference to a file in a bucket, used to perform operations on that file."""
_storage: Storage
@@ -200,14 +283,18 @@
Methods
Will create the file if it doesn't already exist.
"""
try:
- await self._storage._storage_stub.write(bucket_name=self._bucket, key=self.key, body=body)
+ await self._storage._storage_stub.write(
+ storage_write_request=StorageWriteRequest(bucket_name=self._bucket, key=self.key, body=body)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
async def read(self) -> bytes:
"""Read this files contents from the bucket."""
try:
- response = await self._storage._storage_stub.read(bucket_name=self._bucket, key=self.key)
+ response = await self._storage._storage_stub.read(
+ storage_read_request=StorageReadRequest(bucket_name=self._bucket, key=self.key)
+ )
return response.body
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -215,7 +302,29 @@
Methods
async def delete(self):
"""Delete this file from the bucket."""
try:
- await self._storage._storage_stub.delete(bucket_name=self._bucket, key=self.key)
+ await self._storage._storage_stub.delete(
+ storage_delete_request=StorageDeleteRequest(bucket_name=self._bucket, key=self.key)
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ async def upload_url(self, expiry: int = 600):
+ """Get a temporary writable URL to this file."""
+ await self.sign_url(mode=FileMode.WRITE, expiry=expiry)
+
+ async def download_url(self, expiry: int = 600):
+ """Get a temporary readable URL to this file."""
+ await self.sign_url(mode=FileMode.READ, expiry=expiry)
+
+ async def sign_url(self, mode: FileMode = FileMode.READ, expiry: int = 3600):
+ """Generate a signed URL for reading or writing to a file."""
+ try:
+ response = await self._storage._storage_stub.pre_sign_url(
+ storage_pre_sign_url_request=StoragePreSignUrlRequest(
+ bucket_name=self._bucket, key=self.key, operation=mode.to_request_operation(), expiry=expiry
+ )
+ )
+ return response.url
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
@@ -240,11 +349,27 @@
Methods
async def delete(self):
"""Delete this file from the bucket."""
try:
- await self._storage._storage_stub.delete(bucket_name=self._bucket, key=self.key)
+ await self._storage._storage_stub.delete(
+ storage_delete_request=StorageDeleteRequest(bucket_name=self._bucket, key=self.key)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
+
+async def download_url(self, expiry: int = 600)
+
+
+
Get a temporary readable URL to this file.
+
+
+Expand source code
+
+
async def download_url(self, expiry: int = 600):
+ """Get a temporary readable URL to this file."""
+ await self.sign_url(mode=FileMode.READ, expiry=expiry)
+
+
async def read(self) ‑> bytes
@@ -257,12 +382,50 @@
Methods
async def read(self) -> bytes:
"""Read this files contents from the bucket."""
try:
- response = await self._storage._storage_stub.read(bucket_name=self._bucket, key=self.key)
+ response = await self._storage._storage_stub.read(
+ storage_read_request=StorageReadRequest(bucket_name=self._bucket, key=self.key)
+ )
return response.body
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
Generate a signed URL for reading or writing to a file.
+
+
+Expand source code
+
+
async def sign_url(self, mode: FileMode = FileMode.READ, expiry: int = 3600):
+ """Generate a signed URL for reading or writing to a file."""
+ try:
+ response = await self._storage._storage_stub.pre_sign_url(
+ storage_pre_sign_url_request=StoragePreSignUrlRequest(
+ bucket_name=self._bucket, key=self.key, operation=mode.to_request_operation(), expiry=expiry
+ )
+ )
+ return response.url
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+
+
+async def upload_url(self, expiry: int = 600)
+
+
+
Get a temporary writable URL to this file.
+
+
+Expand source code
+
+
async def upload_url(self, expiry: int = 600):
+ """Get a temporary writable URL to this file."""
+ await self.sign_url(mode=FileMode.WRITE, expiry=expiry)
+
+
async def write(self, body: bytes)
@@ -280,13 +443,78 @@
Methods
Will create the file if it doesn't already exist.
"""
try:
- await self._storage._storage_stub.write(bucket_name=self._bucket, key=self.key, body=body)
+ await self._storage._storage_stub.write(
+ storage_write_request=StorageWriteRequest(bucket_name=self._bucket, key=self.key, body=body)
+ )
except GRPCError as grpc_err:
raise exception_from_grpc_error(grpc_err)
def bucket(self, name: str):
"""Return a reference to a bucket from the connected storage service."""
- return Bucket(_storage=self, name=name)
+ return BucketRef(_storage=self, name=name)
Methods
@@ -332,7 +560,7 @@
Methods
def bucket(self, name: str):
"""Return a reference to a bucket from the connected storage service."""
- return Bucket(_storage=self, name=name)
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import asyncio
+from os import getenv, environ
+
+from opentelemetry import trace
+from opentelemetry.sdk.trace import TracerProvider, sampling
+from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
+from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
+
+from nitric.faas import FunctionServer
+from nitric.api.exception import NitricUnavailableException
+
+# from nitric.resources.base import BaseResource
+from typing import Dict, List, Type, Any, TypeVar
+
+
+BT = TypeVar("BT")
+
+
+class Nitric:
+ """Represents a nitric app."""
+
+ _workers: List[FunctionServer] = []
+ _cache: Dict[str, Dict[str, Any]] = {
+ "api": {},
+ "bucket": {},
+ "topic": {},
+ "secret": {},
+ "queue": {},
+ "collection": {},
+ }
+
+ @classmethod
+ def _register_worker(cls, srv: FunctionServer):
+ """Register a worker for this application."""
+ cls._workers.append(srv)
+
+ @classmethod
+ def _create_resource(cls, resource: Type[BT], name: str, *args, **kwargs) -> BT:
+ try:
+ resource_type = resource.__name__.lower()
+ if cls._cache.get(resource_type).get(name) is None:
+ cls._cache[resource_type][name] = resource.make(name, *args, **kwargs)
+
+ return cls._cache[resource_type][name]
+ except ConnectionRefusedError:
+ raise NitricUnavailableException(
+ 'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
+ )
+
+ @classmethod
+ def _create_tracer(cls) -> TracerProvider:
+ local_run = "OTELCOL_BIN" not in environ
+ samplePercent = int(getenv("NITRIC_TRACE_SAMPLE_PERCENT", "100")) / 100.0
+
+ # If its a local run use a console exporter, otherwise export using OTEL Protocol
+ exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
+ if local_run:
+ exporter = ConsoleSpanExporter()
+
+ provider = TracerProvider(
+ active_span_processor=BatchSpanProcessor(exporter),
+ sampler=sampling.TraceIdRatioBased(samplePercent),
+ )
+ trace.set_tracer_provider(provider)
+
+ grpc_client_instrumentor = GrpcInstrumentorClient()
+ grpc_client_instrumentor.instrument()
+
+ return provider
+
+ @classmethod
+ def run(cls):
+ """
+ Start the nitric application.
+
+ This will execute in an existing event loop if there is one, otherwise it will attempt to create its own.
+ """
+ provider = cls._create_tracer()
+ try:
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+
+ loop.run_until_complete(asyncio.gather(*[wkr.start() for wkr in cls._workers]))
+ except KeyboardInterrupt:
+ print("\nexiting")
+ except ConnectionRefusedError:
+ raise NitricUnavailableException(
+ 'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
+ )
+ finally:
+ if provider is not None:
+ provider.force_flush()
+
+
+
+
+
+
+
+
+
+
Classes
+
+
+class Nitric
+
+
+
Represents a nitric app.
+
+
+Expand source code
+
+
class Nitric:
+ """Represents a nitric app."""
+
+ _workers: List[FunctionServer] = []
+ _cache: Dict[str, Dict[str, Any]] = {
+ "api": {},
+ "bucket": {},
+ "topic": {},
+ "secret": {},
+ "queue": {},
+ "collection": {},
+ }
+
+ @classmethod
+ def _register_worker(cls, srv: FunctionServer):
+ """Register a worker for this application."""
+ cls._workers.append(srv)
+
+ @classmethod
+ def _create_resource(cls, resource: Type[BT], name: str, *args, **kwargs) -> BT:
+ try:
+ resource_type = resource.__name__.lower()
+ if cls._cache.get(resource_type).get(name) is None:
+ cls._cache[resource_type][name] = resource.make(name, *args, **kwargs)
+
+ return cls._cache[resource_type][name]
+ except ConnectionRefusedError:
+ raise NitricUnavailableException(
+ 'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
+ )
+
+ @classmethod
+ def _create_tracer(cls) -> TracerProvider:
+ local_run = "OTELCOL_BIN" not in environ
+ samplePercent = int(getenv("NITRIC_TRACE_SAMPLE_PERCENT", "100")) / 100.0
+
+ # If its a local run use a console exporter, otherwise export using OTEL Protocol
+ exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
+ if local_run:
+ exporter = ConsoleSpanExporter()
+
+ provider = TracerProvider(
+ active_span_processor=BatchSpanProcessor(exporter),
+ sampler=sampling.TraceIdRatioBased(samplePercent),
+ )
+ trace.set_tracer_provider(provider)
+
+ grpc_client_instrumentor = GrpcInstrumentorClient()
+ grpc_client_instrumentor.instrument()
+
+ return provider
+
+ @classmethod
+ def run(cls):
+ """
+ Start the nitric application.
+
+ This will execute in an existing event loop if there is one, otherwise it will attempt to create its own.
+ """
+ provider = cls._create_tracer()
+ try:
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+
+ loop.run_until_complete(asyncio.gather(*[wkr.start() for wkr in cls._workers]))
+ except KeyboardInterrupt:
+ print("\nexiting")
+ except ConnectionRefusedError:
+ raise NitricUnavailableException(
+ 'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
+ )
+ finally:
+ if provider is not None:
+ provider.force_flush()
+
+
Static methods
+
+
+def run()
+
+
+
Start the nitric application.
+
This will execute in an existing event loop if there is one, otherwise it will attempt to create its own.
+
+
+Expand source code
+
+
@classmethod
+def run(cls):
+ """
+ Start the nitric application.
+
+ This will execute in an existing event loop if there is one, otherwise it will attempt to create its own.
+ """
+ provider = cls._create_tracer()
+ try:
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+
+ loop.run_until_complete(asyncio.gather(*[wkr.start() for wkr in cls._workers]))
+ except KeyboardInterrupt:
+ print("\nexiting")
+ except ConnectionRefusedError:
+ raise NitricUnavailableException(
+ 'Unable to connect to a nitric server! If you\'re running locally make sure to run "nitric start"'
+ )
+ finally:
+ if provider is not None:
+ provider.force_flush()
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/config/default_settings.html b/docs/nitric/config/default_settings.html
index 2626ea4..38e8475 100644
--- a/docs/nitric/config/default_settings.html
+++ b/docs/nitric/config/default_settings.html
@@ -44,13 +44,11 @@
Module nitric.config.default_settings
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import os
# Provides a set of default settings that env vars can replace
-
# Nitric Membrane Service Address
-SERVICE_BIND = "127.0.0.1:50051"
-# Child Process (User Code) Host Address
-CHILD_ADDRESS = "127.0.0.1:8080"
+SERVICE_BIND = os.environ.get('SERVICE_ADDRESS', '127.0.0.1:50051');
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+from enum import Enum
import functools
import json
import traceback
-from typing import Dict, Union, List, TypeVar, Callable, Coroutine, Any
+from typing import Dict, Union, List, TypeVar, Callable, Coroutine, Any, Optional
+from opentelemetry import context, propagate
import betterproto
from betterproto.grpc.util.async_channel import AsyncChannel
from nitric.utils import new_default_channel
-from proto.nitric.faas.v1 import (
+from nitric.proto.nitric.faas.v1 import (
FaasServiceStub,
InitRequest,
ClientMessage,
TriggerRequest,
TriggerResponse,
+ HeaderValue,
HttpResponseContext,
TopicResponseContext,
+ ScheduleWorker,
+ ApiWorker,
+ SubscriptionWorker,
+ ScheduleRate,
)
+import grpclib
import asyncio
from abc import ABC
Record = Dict[str, Union[str, List[str]]]
+PROPAGATOR = propagate.get_global_textmap()
+
+
+class HttpMethod(Enum):
+ """Valid query expression operators."""
+
+ GET = "GET"
+ POST = "POST"
+ PUT = "PUT"
+ DELETE = "DELETE"
+ PATCH = "PATCH"
+ OPTIONS = "OPTIONS"
+
+ def __str__(self):
+ return str(self.value)
class Request(ABC):
@@ -98,12 +139,13 @@
Module nitric.faas
"""
if ctx.http():
ctx = ctx.http()
+ headers = {k: HeaderValue(value=v) for (k, v) in ctx.res.headers.items()}
+ headers_old = {k: v[0] for (k, v) in ctx.res.headers.items()}
+ data = ctx.res.body if ctx.res.body else bytes()
+
return TriggerResponse(
- data=ctx.res.body,
- http=HttpResponseContext(
- status=ctx.res.status,
- headers=ctx.res.headers,
- ),
+ data=data,
+ http=HttpResponseContext(status=ctx.res.status, headers=headers, headers_old=headers_old),
)
elif ctx.event():
ctx = ctx.event()
@@ -122,13 +164,34 @@
Module nitric.faas
class HttpRequest(Request):
"""Represents a translated Http Request forwarded from the Nitric Membrane."""
- def __init__(self, data: bytes, method: str, path: str, query: Record, headers: Record):
+ def __init__(
+ self,
+ data: bytes,
+ method: str,
+ path: str,
+ params: Dict[str, str],
+ query: Record,
+ headers: Record,
+ trace_context: Dict[str, str],
+ ):
"""Construct a new HttpRequest."""
super().__init__(data)
self.method = method
self.path = path
+ self.params = params
self.query = query
self.headers = headers
+ self.trace_context = trace_context
+
+ @property
+ def json(self) -> Optional[Any]:
+ """Get the body of the request as JSON, returns None if request body is not JSON."""
+ try:
+ return json.loads(self.body)
+ except json.JSONDecodeError:
+ return None
+ except TypeError:
+ return None
@property
def body(self):
@@ -137,13 +200,28 @@
Module nitric.faas
class HttpResponse(Response):
- """Represents an Http Response to be generated by the Nitric Membrane in response to an Http Request Trigger."""
+ """Represents an HTTP Response to be generated by the Nitric Membrane in response to an HTTP Request Trigger."""
def __init__(self, status: int = 200, headers: Record = None, body: bytes = None):
"""Construct a new HttpResponse."""
self.status = status
self.headers = headers if headers else {}
- self.body = body if body else bytes()
+ self._body = body if body else bytes()
+
+ @property
+ def body(self):
+ """Return the HTTP response body."""
+ return self._body
+
+ @body.setter
+ def body(self, value: Union[str, bytes, Any]):
+ if isinstance(value, str):
+ self._body = value.encode("utf-8")
+ elif isinstance(value, bytes):
+ self._body = value
+ else:
+ self._body = json.dumps(value).encode("utf-8")
+ self.headers["Content-Type"] = ["application/json"]
class HttpContext(TriggerContext):
@@ -163,31 +241,36 @@
Module nitric.faas
def from_grpc_trigger_request(trigger_request: TriggerRequest) -> HttpContext:
"""Construct a new HttpContext from an Http trigger from the Nitric Membrane."""
if len(trigger_request.http.headers.keys()) > 0:
- headers = {k: v[0].value for (k, v) in trigger_request.http.headers.items()}
+ headers = {k: v.value for (k, v) in trigger_request.http.headers.items()}
else:
headers = trigger_request.http.headers_old
+ if len(trigger_request.http.query_params.keys()) > 0:
+ query = {k: v.value for (k, v) in trigger_request.http.query_params.items()}
+ else:
+ query = trigger_request.http.query_params_old
+
return HttpContext(
request=HttpRequest(
data=trigger_request.data,
method=trigger_request.http.method,
- query=trigger_request.http.query_params,
+ query=query,
path=trigger_request.http.path,
+ params={k: v for (k, v) in trigger_request.http.path_params.items()},
headers=headers,
+ trace_context=trigger_request.trace_context.values,
)
)
-# ====== Events ======
-
-
class EventRequest(Request):
"""Represents a translated Event, from a Subscribed Topic, forwarded from the Nitric Membrane."""
- def __init__(self, data: bytes, topic: str):
+ def __init__(self, data: bytes, topic: str, trace_context: Dict[str, str]):
"""Construct a new EventRequest."""
super().__init__(data)
self.topic = topic
+ self.trace_context = trace_context
@property
def payload(self) -> bytes:
@@ -219,7 +302,13 @@
Module nitric.faas
@staticmethod
def from_grpc_trigger_request(trigger_request: TriggerRequest):
"""Construct a new EventContext from an Event trigger from the Nitric Membrane."""
- return EventContext(request=EventRequest(data=trigger_request.data, topic=trigger_request.topic.topic))
+ return EventContext(
+ request=EventRequest(
+ data=trigger_request.data,
+ topic=trigger_request.topic.topic,
+ trace_context=trigger_request.trace_context.values,
+ )
+ )
# async def face(inpp: int) -> str:
@@ -231,11 +320,11 @@
The resulting middleware will effectively be a chain of the provided middleware,
where each calls the next in the chain when they're successful.
"""
+ middlewares = list(middlewares)
if len(middlewares) == 1 and not isinstance(middlewares[0], list):
return middlewares[0]
middlewares = [compose_middleware(m) if isinstance(m, list) else m for m in middlewares]
async def handler(ctx, next_middleware=lambda ctx: ctx):
- middleware_chain = functools.reduce(
- lambda acc_next, cur: lambda context: cur(context, acc_next), reversed(middlewares + (next_middleware,))
- )
- return middleware_chain(ctx)
+ def reduce_chain(acc_next, cur):
+ async def chained_middleware(context):
+ # Count the positional arguments to determine if the function is a handler or middleware.
+ all_args = cur.__code__.co_argcount
+ kwargs = len(cur.__defaults__) if cur.__defaults__ is not None else 0
+ pos_args = all_args - kwargs
+ if pos_args == 2:
+ # Call the middleware with next and return the result
+ return (
+ (await cur(context, acc_next)) if asyncio.iscoroutinefunction(cur) else cur(context, acc_next)
+ )
+ else:
+ # Call the handler with ctx only, then call the remainder of the middleware chain
+ result = (await cur(context)) if asyncio.iscoroutinefunction(cur) else cur(context)
+ return (await acc_next(result)) if asyncio.iscoroutinefunction(acc_next) else acc_next(result)
+
+ return chained_middleware
+
+ middleware_chain = functools.reduce(reduce_chain, reversed(middlewares + [next_middleware]))
+ return await middleware_chain(ctx)
return handler
@@ -273,14 +379,86 @@
Module nitric.faas
raise Exception(f"Unknown trigger type: {context_type}, unable to generate expected response")
+class ApiWorkerOptions:
+ """Options for API workers."""
+
+ def __init__(self, api: str, route: str, methods: List[Union[str, HttpMethod]], opts: MethodOptions):
+ """Construct a new options object."""
+ self.api = api
+ self.route = route
+ self.methods = [str(method) for method in methods]
+ self.opts = opts
+
+
+class RateWorkerOptions:
+ """Options for rate workers."""
+
+ description: str
+ rate: int
+ frequency: Frequency
+
+ def __init__(self, description: str, rate: int, frequency: Frequency):
+ """Construct a new options object."""
+ self.description = description
+ self.rate = rate
+ self.frequency = frequency
+
+
+class SubscriptionWorkerOptions:
+ """Options for subscription workers."""
+
+ def __init__(self, topic: str):
+ """Construct a new options object."""
+ self.topic = topic
+
+
+class Frequency(Enum):
+ """Valid schedule frequencies."""
+
+ seconds = "seconds"
+ minutes = "minutes"
+ hours = "hours"
+ days = "days"
+
+ @staticmethod
+ def from_str(value: str) -> Frequency:
+ """Convert a string frequency value to a Frequency."""
+ return Frequency[value.strip().lower()]
+
+ @staticmethod
+ def as_str_list() -> List[str]:
+ """Return all frequency values as a list of strings."""
+ return [str(frequency.value) for frequency in Frequency]
+
+
+class MethodOptions:
+ """Represents options when defining a method handler."""
+
+ security: dict[str, List[str]]
+
+ def __init__(self, security: dict[str, List[str]] = None):
+ """Construct a new HTTP method options object."""
+ self.security = security
+
+
+class FaasWorkerOptions:
+ """Empty worker options for generic function handlers."""
+
+ pass
+
+
+FaasClientOptions = Union[ApiWorkerOptions, RateWorkerOptions, SubscriptionWorkerOptions, FaasWorkerOptions]
+
+
class FunctionServer:
"""A Function as a Service server, which acts as a faas handler for the Nitric Membrane."""
- def __init__(self):
+ def __init__(self, opts: FaasClientOptions):
"""Construct a new function server."""
self.__http_handler = None
self.__event_handler = None
self._any_handler = None
+ self._opts = opts
def http(self, *handlers: Union[Middleware, List[Middleware]]) -> FunctionServer:
"""
@@ -300,14 +478,13 @@
Module nitric.faas
self.__event_handler = compose_middleware(*handlers)
return self
- def start(self, *handlers: Union[Middleware, List[Middleware]]):
+ async def start(self, *handlers: Union[Middleware, List[Middleware]]):
"""Start the function server using the provided trigger handlers."""
self._any_handler = compose_middleware(*handlers) if len(handlers) > 0 else None
- # TODO: implement the server
if not self._any_handler and not self._http_handler and not self._event_handler:
raise Exception("At least one handler function must be provided.")
- asyncio.run(self.run())
+ await self._run()
@property
def _http_handler(self):
@@ -317,14 +494,31 @@
Module nitric.faas
def _event_handler(self):
return self.__event_handler if self.__event_handler else self._any_handler
- async def run(self):
+ async def _run(self):
"""Register a new FaaS worker with the Membrane, using the provided function as the handler."""
channel = new_default_channel()
client = FaasServiceStub(channel)
request_channel = AsyncChannel(close=True)
# We can start be sending all the requests we already have
try:
- await request_channel.send(ClientMessage(init_request=InitRequest()))
+ init_request = InitRequest()
+ # Construct init request based on API worker options
+ if isinstance(self._opts, ApiWorkerOptions):
+ init_request = InitRequest(
+ api=ApiWorker(api=self._opts.api, path=self._opts.route, methods=self._opts.methods)
+ )
+ elif isinstance(self._opts, RateWorkerOptions):
+ # TODO: Populate rate
+ init_request = InitRequest(
+ schedule=ScheduleWorker(
+ key=self._opts.description, rate=ScheduleRate(rate=f"{self._opts.rate} {self._opts.frequency}")
+ )
+ )
+ elif isinstance(self._opts, SubscriptionWorkerOptions):
+ init_request = InitRequest(subscription=SubscriptionWorker(topic=self._opts.topic))
+
+ # let the membrane server know we're ready to start
+ await request_channel.send(ClientMessage(init_request=init_request))
async for srv_msg in client.trigger_stream(request_channel):
# The response iterator will remain active until the connection is closed
msg_type, val = betterproto.which_one_of(srv_msg, "content")
@@ -338,13 +532,21 @@
Module nitric.faas
ctx = _ctx_from_grpc_trigger_request(srv_msg.trigger_request)
try:
+ if len(ctx.req.trace_context) > 0:
+ context.attach(PROPAGATOR().extract(ctx.req.trace_context))
+
if ctx.http():
func = self._http_handler
elif ctx.event():
func = self._event_handler
else:
func = self._any_handler
+
response_ctx = (await func(ctx)) if asyncio.iscoroutinefunction(func) else func(ctx)
+
+ if response_ctx is None:
+ response_ctx = ctx
+
# Send function response back to server
await request_channel.send(
ClientMessage(
@@ -364,6 +566,11 @@
Module nitric.faas
continue
if request_channel.done():
break
+ except grpclib.exceptions.StreamTerminatedError:
+ print("stream from Membrane closed, closing client stream")
+ except asyncio.CancelledError:
+ # Membrane has closed stream after init
+ print("stream from Membrane closed, closing client stream")
except ConnectionRefusedError as cre:
traceback.print_exc()
raise ConnectionRefusedError("Failed to register function with Membrane") from cre
@@ -371,7 +578,6 @@
Module nitric.faas
traceback.print_exc()
raise Exception("An unexpected error occurred.") from e
finally:
- print("stream from Membrane closed, closing client stream")
# The channel must be closed to complete the gRPC connection
request_channel.close()
channel.close()
@@ -386,7 +592,7 @@
Module nitric.faas
When multiple handlers are provided, they will be called in order.
"""
- return FunctionServer().http(*handlers)
+ return FunctionServer(opts=[]).http(*handlers)
def event(*handlers: Union[Middleware, List[Middleware]]) -> FunctionServer:
@@ -395,14 +601,14 @@
Module nitric.faas
When multiple handlers are provided, they will be called in order.
"""
- return FunctionServer().event(*handlers)
+ return FunctionServer(opts=[]).event(*handlers)
def start(*handlers: Union[Middleware, List[Middleware]]):
"""Create a new Function Server and start it using the provided trigger handlers."""
if len(handlers) < 1:
raise Exception("At least one handler must be provided.")
- return FunctionServer().start(*handlers)
+ return FunctionServer(opts=[]).start(*handlers)
@@ -430,16 +636,33 @@
Functions
The resulting middleware will effectively be a chain of the provided middleware,
where each calls the next in the chain when they're successful.
"""
+ middlewares = list(middlewares)
if len(middlewares) == 1 and not isinstance(middlewares[0], list):
return middlewares[0]
middlewares = [compose_middleware(m) if isinstance(m, list) else m for m in middlewares]
async def handler(ctx, next_middleware=lambda ctx: ctx):
- middleware_chain = functools.reduce(
- lambda acc_next, cur: lambda context: cur(context, acc_next), reversed(middlewares + (next_middleware,))
- )
- return middleware_chain(ctx)
+ def reduce_chain(acc_next, cur):
+ async def chained_middleware(context):
+ # Count the positional arguments to determine if the function is a handler or middleware.
+ all_args = cur.__code__.co_argcount
+ kwargs = len(cur.__defaults__) if cur.__defaults__ is not None else 0
+ pos_args = all_args - kwargs
+ if pos_args == 2:
+ # Call the middleware with next and return the result
+ return (
+ (await cur(context, acc_next)) if asyncio.iscoroutinefunction(cur) else cur(context, acc_next)
+ )
+ else:
+ # Call the handler with ctx only, then call the remainder of the middleware chain
+ result = (await cur(context)) if asyncio.iscoroutinefunction(cur) else cur(context)
+ return (await acc_next(result)) if asyncio.iscoroutinefunction(acc_next) else acc_next(result)
+
+ return chained_middleware
+
+ middleware_chain = functools.reduce(reduce_chain, reversed(middlewares + [next_middleware]))
+ return await middleware_chain(ctx)
return handler
@@ -460,7 +683,7 @@
Functions
When multiple handlers are provided, they will be called in order.
"""
- return FunctionServer().event(*handlers)
+ return FunctionServer(opts=[]).event(*handlers)
@@ -479,7 +702,7 @@
Functions
When multiple handlers are provided, they will be called in order.
"""
- return FunctionServer().http(*handlers)
+ return FunctionServer(opts=[]).http(*handlers)
@@ -495,7 +718,7 @@
Functions
"""Create a new Function Server and start it using the provided trigger handlers."""
if len(handlers) < 1:
raise Exception("At least one handler must be provided.")
- return FunctionServer().start(*handlers)
+ return FunctionServer(opts=[]).start(*handlers)
@@ -503,6 +726,28 @@
@staticmethod
def from_grpc_trigger_request(trigger_request: TriggerRequest):
"""Construct a new EventContext from an Event trigger from the Nitric Membrane."""
- return EventContext(request=EventRequest(data=trigger_request.data, topic=trigger_request.topic.topic))
+ return EventContext(
+ request=EventRequest(
+ data=trigger_request.data,
+ topic=trigger_request.topic.topic,
+ trace_context=trigger_request.trace_context.values,
+ )
+ )
Ancestors
@@ -551,7 +802,13 @@
Static methods
@staticmethod
def from_grpc_trigger_request(trigger_request: TriggerRequest):
"""Construct a new EventContext from an Event trigger from the Nitric Membrane."""
- return EventContext(request=EventRequest(data=trigger_request.data, topic=trigger_request.topic.topic))
Represents a translated Event, from a Subscribed Topic, forwarded from the Nitric Membrane.
@@ -595,10 +852,11 @@
Inherited members
class EventRequest(Request):
"""Represents a translated Event, from a Subscribed Topic, forwarded from the Nitric Membrane."""
- def __init__(self, data: bytes, topic: str):
+ def __init__(self, data: bytes, topic: str, trace_context: Dict[str, str]):
"""Construct a new EventRequest."""
super().__init__(data)
self.topic = topic
+ self.trace_context = trace_context
@property
def payload(self) -> bytes:
@@ -651,8 +909,109 @@
Ancestors
abc.ABC
+
+class FaasWorkerOptions
+
+
+
Empty worker options for generic function handlers.
+
+
+Expand source code
+
+
class FaasWorkerOptions:
+ """Empty worker options for generic function handlers."""
+
+ pass
+
+
+
+class Frequency
+(value, names=None, *, module=None, qualname=None, type=None, start=1)
+
+
+
Valid schedule frequencies.
+
+
+Expand source code
+
+
class Frequency(Enum):
+ """Valid schedule frequencies."""
+
+ seconds = "seconds"
+ minutes = "minutes"
+ hours = "hours"
+ days = "days"
+
+ @staticmethod
+ def from_str(value: str) -> Frequency:
+ """Convert a string frequency value to a Frequency."""
+ return Frequency[value.strip().lower()]
+
+ @staticmethod
+ def as_str_list() -> List[str]:
+ """Return all frequency values as a list of strings."""
+ return [str(frequency.value) for frequency in Frequency]
+
+
Ancestors
+
+
enum.Enum
+
+
Class variables
+
+
var days
+
+
+
+
var hours
+
+
+
+
var minutes
+
+
+
+
var seconds
+
+
+
+
+
Static methods
+
+
+def as_str_list() ‑> List[str]
+
+
+
Return all frequency values as a list of strings.
+
+
+Expand source code
+
+
@staticmethod
+def as_str_list() -> List[str]:
+ """Return all frequency values as a list of strings."""
+ return [str(frequency.value) for frequency in Frequency]
@staticmethod
+def from_str(value: str) -> Frequency:
+ """Convert a string frequency value to a Frequency."""
+ return Frequency[value.strip().lower()]
+
+
+
+
class FunctionServer
+(opts: FaasClientOptions)
A Function as a Service server, which acts as a faas handler for the Nitric Membrane.
@@ -664,11 +1023,12 @@
Ancestors
class FunctionServer:
"""A Function as a Service server, which acts as a faas handler for the Nitric Membrane."""
- def __init__(self):
+ def __init__(self, opts: FaasClientOptions):
"""Construct a new function server."""
self.__http_handler = None
self.__event_handler = None
self._any_handler = None
+ self._opts = opts
def http(self, *handlers: Union[Middleware, List[Middleware]]) -> FunctionServer:
"""
@@ -688,14 +1048,13 @@
Ancestors
self.__event_handler = compose_middleware(*handlers)
return self
- def start(self, *handlers: Union[Middleware, List[Middleware]]):
+ async def start(self, *handlers: Union[Middleware, List[Middleware]]):
"""Start the function server using the provided trigger handlers."""
self._any_handler = compose_middleware(*handlers) if len(handlers) > 0 else None
- # TODO: implement the server
if not self._any_handler and not self._http_handler and not self._event_handler:
raise Exception("At least one handler function must be provided.")
- asyncio.run(self.run())
+ await self._run()
@property
def _http_handler(self):
@@ -705,14 +1064,31 @@
Ancestors
def _event_handler(self):
return self.__event_handler if self.__event_handler else self._any_handler
- async def run(self):
+ async def _run(self):
"""Register a new FaaS worker with the Membrane, using the provided function as the handler."""
channel = new_default_channel()
client = FaasServiceStub(channel)
request_channel = AsyncChannel(close=True)
# We can start be sending all the requests we already have
try:
- await request_channel.send(ClientMessage(init_request=InitRequest()))
+ init_request = InitRequest()
+ # Construct init request based on API worker options
+ if isinstance(self._opts, ApiWorkerOptions):
+ init_request = InitRequest(
+ api=ApiWorker(api=self._opts.api, path=self._opts.route, methods=self._opts.methods)
+ )
+ elif isinstance(self._opts, RateWorkerOptions):
+ # TODO: Populate rate
+ init_request = InitRequest(
+ schedule=ScheduleWorker(
+ key=self._opts.description, rate=ScheduleRate(rate=f"{self._opts.rate} {self._opts.frequency}")
+ )
+ )
+ elif isinstance(self._opts, SubscriptionWorkerOptions):
+ init_request = InitRequest(subscription=SubscriptionWorker(topic=self._opts.topic))
+
+ # let the membrane server know we're ready to start
+ await request_channel.send(ClientMessage(init_request=init_request))
async for srv_msg in client.trigger_stream(request_channel):
# The response iterator will remain active until the connection is closed
msg_type, val = betterproto.which_one_of(srv_msg, "content")
@@ -726,13 +1102,21 @@
Ancestors
ctx = _ctx_from_grpc_trigger_request(srv_msg.trigger_request)
try:
+ if len(ctx.req.trace_context) > 0:
+ context.attach(PROPAGATOR().extract(ctx.req.trace_context))
+
if ctx.http():
func = self._http_handler
elif ctx.event():
func = self._event_handler
else:
func = self._any_handler
+
response_ctx = (await func(ctx)) if asyncio.iscoroutinefunction(func) else func(ctx)
+
+ if response_ctx is None:
+ response_ctx = ctx
+
# Send function response back to server
await request_channel.send(
ClientMessage(
@@ -752,6 +1136,11 @@
Ancestors
continue
if request_channel.done():
break
+ except grpclib.exceptions.StreamTerminatedError:
+ print("stream from Membrane closed, closing client stream")
+ except asyncio.CancelledError:
+ # Membrane has closed stream after init
+ print("stream from Membrane closed, closing client stream")
except ConnectionRefusedError as cre:
traceback.print_exc()
raise ConnectionRefusedError("Failed to register function with Membrane") from cre
@@ -759,7 +1148,6 @@
Ancestors
traceback.print_exc()
raise Exception("An unexpected error occurred.") from e
finally:
- print("stream from Membrane closed, closing client stream")
# The channel must be closed to complete the gRPC connection
request_channel.close()
channel.close()
@@ -806,77 +1194,8 @@
Methods
return self
-
-async def run(self)
-
-
-
Register a new FaaS worker with the Membrane, using the provided function as the handler.
-
-
-Expand source code
-
-
async def run(self):
- """Register a new FaaS worker with the Membrane, using the provided function as the handler."""
- channel = new_default_channel()
- client = FaasServiceStub(channel)
- request_channel = AsyncChannel(close=True)
- # We can start be sending all the requests we already have
- try:
- await request_channel.send(ClientMessage(init_request=InitRequest()))
- async for srv_msg in client.trigger_stream(request_channel):
- # The response iterator will remain active until the connection is closed
- msg_type, val = betterproto.which_one_of(srv_msg, "content")
-
- if msg_type == "init_response":
- print("function connected to Membrane")
- # We don't need to reply
- # proceed to the next available message
- continue
- if msg_type == "trigger_request":
- ctx = _ctx_from_grpc_trigger_request(srv_msg.trigger_request)
-
- try:
- if ctx.http():
- func = self._http_handler
- elif ctx.event():
- func = self._event_handler
- else:
- func = self._any_handler
- response_ctx = (await func(ctx)) if asyncio.iscoroutinefunction(func) else func(ctx)
- # Send function response back to server
- await request_channel.send(
- ClientMessage(
- id=srv_msg.id,
- trigger_response=_grpc_response_from_ctx(response_ctx),
- )
- )
- except Exception:
- # Any unhandled exceptions in the above code will end the loop
- # and stop processing future triggers, we catch them here as a last resort.
- print("An unexpected error occurred processing trigger or response")
- traceback.print_exc()
- response = _create_internal_error_response(srv_msg.trigger_request)
- await request_channel.send(ClientMessage(id=srv_msg.id, trigger_response=response))
- else:
- print(f"unhandled message type {msg_type}, skipping")
- continue
- if request_channel.done():
- break
- except ConnectionRefusedError as cre:
- traceback.print_exc()
- raise ConnectionRefusedError("Failed to register function with Membrane") from cre
- except Exception as e:
- traceback.print_exc()
- raise Exception("An unexpected error occurred.") from e
- finally:
- print("stream from Membrane closed, closing client stream")
- # The channel must be closed to complete the gRPC connection
- request_channel.close()
- channel.close()
async def start(self, *handlers: Union[Middleware, List[Middleware]]):
"""Start the function server using the provided trigger handlers."""
self._any_handler = compose_middleware(*handlers) if len(handlers) > 0 else None
- # TODO: implement the server
if not self._any_handler and not self._http_handler and not self._event_handler:
raise Exception("At least one handler function must be provided.")
- asyncio.run(self.run())
+ await self._run()
@@ -924,17 +1242,24 @@
Methods
def from_grpc_trigger_request(trigger_request: TriggerRequest) -> HttpContext:
"""Construct a new HttpContext from an Http trigger from the Nitric Membrane."""
if len(trigger_request.http.headers.keys()) > 0:
- headers = {k: v[0].value for (k, v) in trigger_request.http.headers.items()}
+ headers = {k: v.value for (k, v) in trigger_request.http.headers.items()}
else:
headers = trigger_request.http.headers_old
+ if len(trigger_request.http.query_params.keys()) > 0:
+ query = {k: v.value for (k, v) in trigger_request.http.query_params.items()}
+ else:
+ query = trigger_request.http.query_params_old
+
return HttpContext(
request=HttpRequest(
data=trigger_request.data,
method=trigger_request.http.method,
- query=trigger_request.http.query_params,
+ query=query,
path=trigger_request.http.path,
+ params={k: v for (k, v) in trigger_request.http.path_params.items()},
headers=headers,
+ trace_context=trigger_request.trace_context.values,
)
)
@@ -958,17 +1283,24 @@
Static methods
def from_grpc_trigger_request(trigger_request: TriggerRequest) -> HttpContext:
"""Construct a new HttpContext from an Http trigger from the Nitric Membrane."""
if len(trigger_request.http.headers.keys()) > 0:
- headers = {k: v[0].value for (k, v) in trigger_request.http.headers.items()}
+ headers = {k: v.value for (k, v) in trigger_request.http.headers.items()}
else:
headers = trigger_request.http.headers_old
+ if len(trigger_request.http.query_params.keys()) > 0:
+ query = {k: v.value for (k, v) in trigger_request.http.query_params.items()}
+ else:
+ query = trigger_request.http.query_params_old
+
return HttpContext(
request=HttpRequest(
data=trigger_request.data,
method=trigger_request.http.method,
- query=trigger_request.http.query_params,
+ query=query,
path=trigger_request.http.path,
+ params={k: v for (k, v) in trigger_request.http.path_params.items()},
headers=headers,
+ trace_context=trigger_request.trace_context.values,
)
)
@@ -1000,9 +1332,64 @@
Represents a translated Http Request forwarded from the Nitric Membrane.
@@ -1014,13 +1401,34 @@
Inherited members
class HttpRequest(Request):
"""Represents a translated Http Request forwarded from the Nitric Membrane."""
- def __init__(self, data: bytes, method: str, path: str, query: Record, headers: Record):
+ def __init__(
+ self,
+ data: bytes,
+ method: str,
+ path: str,
+ params: Dict[str, str],
+ query: Record,
+ headers: Record,
+ trace_context: Dict[str, str],
+ ):
"""Construct a new HttpRequest."""
super().__init__(data)
self.method = method
self.path = path
+ self.params = params
self.query = query
self.headers = headers
+ self.trace_context = trace_context
+
+ @property
+ def json(self) -> Optional[Any]:
+ """Get the body of the request as JSON, returns None if request body is not JSON."""
+ try:
+ return json.loads(self.body)
+ except json.JSONDecodeError:
+ return None
+ except TypeError:
+ return None
@property
def body(self):
@@ -1047,6 +1455,24 @@
Instance variables
return self.data.decode("utf-8")
+
var json : Optional[Any]
+
+
Get the body of the request as JSON, returns None if request body is not JSON.
+
+
+Expand source code
+
+
@property
+def json(self) -> Optional[Any]:
+ """Get the body of the request as JSON, returns None if request body is not JSON."""
+ try:
+ return json.loads(self.body)
+ except json.JSONDecodeError:
+ return None
+ except TypeError:
+ return None
+
+
@@ -1054,26 +1480,125 @@
Instance variables
(status: int = 200, headers: Record = None, body: bytes = None)
-
Represents an Http Response to be generated by the Nitric Membrane in response to an Http Request Trigger.
+
Represents an HTTP Response to be generated by the Nitric Membrane in response to an HTTP Request Trigger.
Construct a new HttpResponse.
Expand source code
class HttpResponse(Response):
- """Represents an Http Response to be generated by the Nitric Membrane in response to an Http Request Trigger."""
+ """Represents an HTTP Response to be generated by the Nitric Membrane in response to an HTTP Request Trigger."""
def __init__(self, status: int = 200, headers: Record = None, body: bytes = None):
"""Construct a new HttpResponse."""
self.status = status
self.headers = headers if headers else {}
- self.body = body if body else bytes()
+ self._body = body if body else bytes()
+
+ @property
+ def body(self):
+ """Return the HTTP response body."""
+ return self._body
+
+ @body.setter
+ def body(self, value: Union[str, bytes, Any]):
+ if isinstance(value, str):
+ self._body = value.encode("utf-8")
+ elif isinstance(value, bytes):
+ self._body = value
+ else:
+ self._body = json.dumps(value).encode("utf-8")
+ self.headers["Content-Type"] = ["application/json"]
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/deploy/index.html b/docs/nitric/proto/nitric/deploy/index.html
new file mode 100644
index 0000000..f3022f1
--- /dev/null
+++ b/docs/nitric/proto/nitric/deploy/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.deploy API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.deploy
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/deploy/v1/index.html b/docs/nitric/proto/nitric/deploy/v1/index.html
new file mode 100644
index 0000000..3b307f6
--- /dev/null
+++ b/docs/nitric/proto/nitric/deploy/v1/index.html
@@ -0,0 +1,1550 @@
+
+
+
+
+
+
+nitric.proto.nitric.deploy.v1 API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.deploy.v1
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# sources: proto/deploy/v1/deploy.proto
+# plugin: python-betterproto
+import warnings
+from dataclasses import dataclass
+from typing import (
+ TYPE_CHECKING,
+ AsyncIterator,
+ Dict,
+ List,
+ Optional,
+)
+
+import betterproto
+import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf
+import grpclib
+from betterproto.grpc.grpclib_server import ServiceBase
+
+from ...resource import v1 as __resource_v1__
+
+
+if TYPE_CHECKING:
+ import grpclib.server
+ from betterproto.grpc.grpclib_client import MetadataLike
+ from grpclib.metadata import Deadline
+
+
+@dataclass(eq=False, repr=False)
+class DeployUpRequest(betterproto.Message):
+ spec: "Spec" = betterproto.message_field(1)
+ """The spec to deploy"""
+
+ attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(2)
+ """
+ A map of attributes related to the deploy request this allows for adding
+ project identifiers etc.
+ """
+
+
+@dataclass(eq=False, repr=False)
+class DeployUpEvent(betterproto.Message):
+ message: "DeployEventMessage" = betterproto.message_field(1, group="content")
+ result: "DeployUpEventResult" = betterproto.message_field(2, group="content")
+
+
+@dataclass(eq=False, repr=False)
+class DeployEventMessage(betterproto.Message):
+ """Messages to provide status updates on the deployment"""
+
+ message: str = betterproto.string_field(1)
+
+
+@dataclass(eq=False, repr=False)
+class UpResult(betterproto.Message):
+ string_result: str = betterproto.string_field(1, group="content")
+ """Simple formatted string output as result"""
+
+
+@dataclass(eq=False, repr=False)
+class DeployUpEventResult(betterproto.Message):
+ """Terminal message indicating deployment success"""
+
+ success: bool = betterproto.bool_field(1)
+ """Indicate the success status"""
+
+ result: "UpResult" = betterproto.message_field(2)
+ """
+ Output state as a struct, this can be provided as an output file or pretty
+ printed for CLI output
+ """
+
+
+@dataclass(eq=False, repr=False)
+class DeployDownRequest(betterproto.Message):
+ attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(1)
+ """
+ A map of attributes related to the deploy request this allows for adding
+ project identifiers etc.
+ """
+
+
+@dataclass(eq=False, repr=False)
+class DeployDownEvent(betterproto.Message):
+ message: "DeployEventMessage" = betterproto.message_field(1, group="content")
+ result: "DeployDownEventResult" = betterproto.message_field(2, group="content")
+
+
+@dataclass(eq=False, repr=False)
+class DeployDownEventResult(betterproto.Message):
+ """Terminal message indicating deployment success"""
+
+ pass
+
+
+@dataclass(eq=False, repr=False)
+class ImageSource(betterproto.Message):
+ """An image source to be used for execution unit deployment"""
+
+ uri: str = betterproto.string_field(1)
+ """
+ URI of the docker image TODO: May also need to provide auth information
+ (although this should just be configured on the running client already)
+ """
+
+
+@dataclass(eq=False, repr=False)
+class ExecutionUnit(betterproto.Message):
+ """A unit of execution (i.e. function/container)"""
+
+ image: "ImageSource" = betterproto.message_field(1, group="source")
+ """Container image as a execution unit"""
+
+ workers: int = betterproto.int32_field(10)
+ """Expected worker count for this execution unit"""
+
+ timeout: int = betterproto.int32_field(11)
+ """Configurable timeout for request handling"""
+
+ memory: int = betterproto.int32_field(12)
+ """Configurable memory size for this instance"""
+
+ type: str = betterproto.string_field(13)
+ """
+ A simple type property describes the requested type of execution unit that
+ this should be for this project, a provider can implement how this request
+ is satisfied in any way
+ """
+
+ env: Dict[str, str] = betterproto.map_field(
+ 14, betterproto.TYPE_STRING, betterproto.TYPE_STRING
+ )
+ """Environment variables for this execution unit"""
+
+ def __post_init__(self) -> None:
+ super().__post_init__()
+ if self.is_set("timeout"):
+ warnings.warn("ExecutionUnit.timeout is deprecated", DeprecationWarning)
+ if self.is_set("memory"):
+ warnings.warn("ExecutionUnit.memory is deprecated", DeprecationWarning)
+
+
+@dataclass(eq=False, repr=False)
+class Bucket(betterproto.Message):
+ pass
+
+
+@dataclass(eq=False, repr=False)
+class Topic(betterproto.Message):
+ subscriptions: List["SubscriptionTarget"] = betterproto.message_field(1)
+ """TODO: Include topic specifications here"""
+
+
+@dataclass(eq=False, repr=False)
+class Queue(betterproto.Message):
+ pass
+
+
+@dataclass(eq=False, repr=False)
+class Collection(betterproto.Message):
+ pass
+
+
+@dataclass(eq=False, repr=False)
+class Secret(betterproto.Message):
+ pass
+
+
+@dataclass(eq=False, repr=False)
+class SubscriptionTarget(betterproto.Message):
+ execution_unit: str = betterproto.string_field(1, group="target")
+ """The name of an execution unit to target"""
+
+
+@dataclass(eq=False, repr=False)
+class TopicSubscription(betterproto.Message):
+ target: "SubscriptionTarget" = betterproto.message_field(1)
+
+
+@dataclass(eq=False, repr=False)
+class Api(betterproto.Message):
+ openapi: str = betterproto.string_field(1, group="document")
+ """
+ An OpenAPI document for deployment This document will contain extensions
+ that hint of execution units that should be targeted as part of the
+ deployment
+ """
+
+
+@dataclass(eq=False, repr=False)
+class ScheduleTarget(betterproto.Message):
+ execution_unit: str = betterproto.string_field(1, group="target")
+ """The name of an execution unit to target"""
+
+
+@dataclass(eq=False, repr=False)
+class Schedule(betterproto.Message):
+ cron: str = betterproto.string_field(1)
+ target: "ScheduleTarget" = betterproto.message_field(2)
+
+
+@dataclass(eq=False, repr=False)
+class Resource(betterproto.Message):
+ name: str = betterproto.string_field(1)
+ type: "__resource_v1__.ResourceType" = betterproto.enum_field(2)
+ execution_unit: "ExecutionUnit" = betterproto.message_field(10, group="config")
+ bucket: "Bucket" = betterproto.message_field(11, group="config")
+ topic: "Topic" = betterproto.message_field(12, group="config")
+ queue: "Queue" = betterproto.message_field(13, group="config")
+ api: "Api" = betterproto.message_field(14, group="config")
+ policy: "Policy" = betterproto.message_field(15, group="config")
+ schedule: "Schedule" = betterproto.message_field(16, group="config")
+ collection: "Collection" = betterproto.message_field(17, group="config")
+ secret: "Secret" = betterproto.message_field(18, group="config")
+
+
+@dataclass(eq=False, repr=False)
+class Policy(betterproto.Message):
+ """
+ TODO: This is already defined in our resource contracts... Need to
+ determine if it's worth re-using unfortunately there are parts we don't
+ want to duplicate, such as API config
+ """
+
+ principals: List["Resource"] = betterproto.message_field(1)
+ actions: List["__resource_v1__.Action"] = betterproto.enum_field(2)
+ """
+ TODO: Split out discrete action definitions from resources Also need to
+ allow custom action types as well Should incorporate action re-use here...
+ """
+
+ resources: List["Resource"] = betterproto.message_field(3)
+
+
+@dataclass(eq=False, repr=False)
+class Spec(betterproto.Message):
+ resources: List["Resource"] = betterproto.message_field(1)
+ """list of resources to deploy"""
+
+
+class DeployServiceStub(betterproto.ServiceStub):
+ async def up(
+ self,
+ deploy_up_request: "DeployUpRequest",
+ *,
+ timeout: Optional[float] = None,
+ deadline: Optional["Deadline"] = None,
+ metadata: Optional["MetadataLike"] = None
+ ) -> AsyncIterator["DeployUpEvent"]:
+ async for response in self._unary_stream(
+ "/nitric.deploy.v1.DeployService/Up",
+ deploy_up_request,
+ DeployUpEvent,
+ timeout=timeout,
+ deadline=deadline,
+ metadata=metadata,
+ ):
+ yield response
+
+ async def down(
+ self,
+ deploy_down_request: "DeployDownRequest",
+ *,
+ timeout: Optional[float] = None,
+ deadline: Optional["Deadline"] = None,
+ metadata: Optional["MetadataLike"] = None
+ ) -> AsyncIterator["DeployDownEvent"]:
+ async for response in self._unary_stream(
+ "/nitric.deploy.v1.DeployService/Down",
+ deploy_down_request,
+ DeployDownEvent,
+ timeout=timeout,
+ deadline=deadline,
+ metadata=metadata,
+ ):
+ yield response
+
+
+class DeployServiceBase(ServiceBase):
+ async def up(
+ self, deploy_up_request: "DeployUpRequest"
+ ) -> AsyncIterator["DeployUpEvent"]:
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
+
+ async def down(
+ self, deploy_down_request: "DeployDownRequest"
+ ) -> AsyncIterator["DeployDownEvent"]:
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
+
+ async def __rpc_up(
+ self, stream: "grpclib.server.Stream[DeployUpRequest, DeployUpEvent]"
+ ) -> None:
+ request = await stream.recv_message()
+ await self._call_rpc_handler_server_stream(
+ self.up,
+ stream,
+ request,
+ )
+
+ async def __rpc_down(
+ self, stream: "grpclib.server.Stream[DeployDownRequest, DeployDownEvent]"
+ ) -> None:
+ request = await stream.recv_message()
+ await self._call_rpc_handler_server_stream(
+ self.down,
+ stream,
+ request,
+ )
+
+ def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
+ return {
+ "/nitric.deploy.v1.DeployService/Up": grpclib.const.Handler(
+ self.__rpc_up,
+ grpclib.const.Cardinality.UNARY_STREAM,
+ DeployUpRequest,
+ DeployUpEvent,
+ ),
+ "/nitric.deploy.v1.DeployService/Down": grpclib.const.Handler(
+ self.__rpc_down,
+ grpclib.const.Cardinality.UNARY_STREAM,
+ DeployDownRequest,
+ DeployDownEvent,
+ ),
+ }
+
+
+
+
+
+
+
+
+
+
Classes
+
+
+class Api
+(openapi: str = <object object>)
+
+
+
Api(openapi: str =
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Api(betterproto.Message):
+ openapi: str = betterproto.string_field(1, group="document")
+ """
+ An OpenAPI document for deployment This document will contain extensions
+ that hint of execution units that should be targeted as part of the
+ deployment
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var openapi : str
+
+
An OpenAPI document for deployment This document will contain extensions
+that hint of execution units that should be targeted as part of the
+deployment
@dataclass(eq=False, repr=False)
+class DeployDownRequest(betterproto.Message):
+ attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(1)
+ """
+ A map of attributes related to the deploy request this allows for adding
+ project identifiers etc.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var attributes : betterproto.lib.google.protobuf.Struct
+
+
A map of attributes related to the deploy request this allows for adding
+project identifiers etc.
Messages to provide status updates on the deployment
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class DeployEventMessage(betterproto.Message):
+ """Messages to provide status updates on the deployment"""
+
+ message: str = betterproto.string_field(1)
@dataclass(eq=False, repr=False)
+class DeployUpEventResult(betterproto.Message):
+ """Terminal message indicating deployment success"""
+
+ success: bool = betterproto.bool_field(1)
+ """Indicate the success status"""
+
+ result: "UpResult" = betterproto.message_field(2)
+ """
+ Output state as a struct, this can be provided as an output file or pretty
+ printed for CLI output
+ """
@dataclass(eq=False, repr=False)
+class DeployUpRequest(betterproto.Message):
+ spec: "Spec" = betterproto.message_field(1)
+ """The spec to deploy"""
+
+ attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(2)
+ """
+ A map of attributes related to the deploy request this allows for adding
+ project identifiers etc.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var attributes : betterproto.lib.google.protobuf.Struct
+
+
A map of attributes related to the deploy request this allows for adding
+project identifiers etc.
@dataclass(eq=False, repr=False)
+class ExecutionUnit(betterproto.Message):
+ """A unit of execution (i.e. function/container)"""
+
+ image: "ImageSource" = betterproto.message_field(1, group="source")
+ """Container image as a execution unit"""
+
+ workers: int = betterproto.int32_field(10)
+ """Expected worker count for this execution unit"""
+
+ timeout: int = betterproto.int32_field(11)
+ """Configurable timeout for request handling"""
+
+ memory: int = betterproto.int32_field(12)
+ """Configurable memory size for this instance"""
+
+ type: str = betterproto.string_field(13)
+ """
+ A simple type property describes the requested type of execution unit that
+ this should be for this project, a provider can implement how this request
+ is satisfied in any way
+ """
+
+ env: Dict[str, str] = betterproto.map_field(
+ 14, betterproto.TYPE_STRING, betterproto.TYPE_STRING
+ )
+ """Environment variables for this execution unit"""
+
+ def __post_init__(self) -> None:
+ super().__post_init__()
+ if self.is_set("timeout"):
+ warnings.warn("ExecutionUnit.timeout is deprecated", DeprecationWarning)
+ if self.is_set("memory"):
+ warnings.warn("ExecutionUnit.memory is deprecated", DeprecationWarning)
A simple type property describes the requested type of execution unit that
+this should be for this project, a provider can implement how this request
+is satisfied in any way
An image source to be used for execution unit deployment
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class ImageSource(betterproto.Message):
+ """An image source to be used for execution unit deployment"""
+
+ uri: str = betterproto.string_field(1)
+ """
+ URI of the docker image TODO: May also need to provide auth information
+ (although this should just be configured on the running client already)
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var uri : str
+
+
URI of the docker image TODO: May also need to provide auth information
+(although this should just be configured on the running client already)
TODO: This is already defined in our resource contracts… Need to
+determine if it's worth re-using unfortunately there are parts we don't
+want to duplicate, such as API config
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Policy(betterproto.Message):
+ """
+ TODO: This is already defined in our resource contracts... Need to
+ determine if it's worth re-using unfortunately there are parts we don't
+ want to duplicate, such as API config
+ """
+
+ principals: List["Resource"] = betterproto.message_field(1)
+ actions: List["__resource_v1__.Action"] = betterproto.enum_field(2)
+ """
+ TODO: Split out discrete action definitions from resources Also need to
+ allow custom action types as well Should incorporate action re-use here...
+ """
+
+ resources: List["Resource"] = betterproto.message_field(3)
@dataclass(eq=False, repr=False)
+class ScheduleTarget(betterproto.Message):
+ execution_unit: str = betterproto.string_field(1, group="target")
+ """The name of an execution unit to target"""
@dataclass(eq=False, repr=False)
+class SubscriptionTarget(betterproto.Message):
+ execution_unit: str = betterproto.string_field(1, group="target")
+ """The name of an execution unit to target"""
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/document/index.html b/docs/nitric/proto/nitric/document/index.html
new file mode 100644
index 0000000..2c9d744
--- /dev/null
+++ b/docs/nitric/proto/nitric/document/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.document API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.document
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/error/index.html b/docs/nitric/proto/nitric/error/index.html
new file mode 100644
index 0000000..68f43c0
--- /dev/null
+++ b/docs/nitric/proto/nitric/error/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.error API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.error
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/error/v1/index.html b/docs/nitric/proto/nitric/error/v1/index.html
new file mode 100644
index 0000000..30322c5
--- /dev/null
+++ b/docs/nitric/proto/nitric/error/v1/index.html
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+nitric.proto.nitric.error.v1 API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.error.v1
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# sources: proto/error/v1/error.proto
+# plugin: python-betterproto
+from dataclasses import dataclass
+from typing import Dict
+
+import betterproto
+
+
+@dataclass(eq=False, repr=False)
+class ErrorScope(betterproto.Message):
+ service: str = betterproto.string_field(1)
+ """The API service invoked, e.g. 'Service.Method'."""
+
+ plugin: str = betterproto.string_field(2)
+ """The plugin method invoked, e.g. 'PluginService.Method'."""
+
+ args: Dict[str, str] = betterproto.map_field(
+ 3, betterproto.TYPE_STRING, betterproto.TYPE_STRING
+ )
+ """
+ The plugin method arguments, ensure only non-sensitive data is specified.
+ """
+
+
+@dataclass(eq=False, repr=False)
+class ErrorDetails(betterproto.Message):
+ message: str = betterproto.string_field(1)
+ """
+ The developer error message, explaining the error and ideally solution.
+ """
+
+ cause: str = betterproto.string_field(2)
+ """The error root cause."""
+
+ scope: "ErrorScope" = betterproto.message_field(3)
+ """The scope of the error."""
@dataclass(eq=False, repr=False)
+class ErrorScope(betterproto.Message):
+ service: str = betterproto.string_field(1)
+ """The API service invoked, e.g. 'Service.Method'."""
+
+ plugin: str = betterproto.string_field(2)
+ """The plugin method invoked, e.g. 'PluginService.Method'."""
+
+ args: Dict[str, str] = betterproto.map_field(
+ 3, betterproto.TYPE_STRING, betterproto.TYPE_STRING
+ )
+ """
+ The plugin method arguments, ensure only non-sensitive data is specified.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var args : Dict[str, str]
+
+
The plugin method arguments, ensure only non-sensitive data is specified.
+
+
var plugin : str
+
+
The plugin method invoked, e.g. 'PluginService.Method'.
+
+
var service : str
+
+
The API service invoked, e.g. 'Service.Method'.
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/event/index.html b/docs/nitric/proto/nitric/event/index.html
new file mode 100644
index 0000000..29bdace
--- /dev/null
+++ b/docs/nitric/proto/nitric/event/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.event API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.event
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class EventPublishRequest(betterproto.Message):
+ """Request to publish an event to a topic"""
+
+ topic: str = betterproto.string_field(1)
+ """The name of the topic to publish the event to"""
+
+ event: "NitricEvent" = betterproto.message_field(2)
+ """The event to be published"""
+
+ delay: int = betterproto.uint32_field(3)
+ """An optional delay specified in seconds (minimum 10 seconds)"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var delay : int
+
+
An optional delay specified in seconds (minimum 10 seconds)
@dataclass(eq=False, repr=False)
+class EventPublishResponse(betterproto.Message):
+ """Result of publishing an event"""
+
+ id: str = betterproto.string_field(1)
+ """
+ The id of the published message When an id was not supplied one should be
+ automatically generated
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var id : str
+
+
The id of the published message When an id was not supplied one should be
+automatically generated
@dataclass(eq=False, repr=False)
+class TopicListResponse(betterproto.Message):
+ """Topic List Response"""
+
+ topics: List["NitricTopic"] = betterproto.message_field(1)
+ """The list of found topics"""
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/faas/index.html b/docs/nitric/proto/nitric/faas/index.html
new file mode 100644
index 0000000..47bf2b3
--- /dev/null
+++ b/docs/nitric/proto/nitric/faas/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.faas API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.faas
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class ApiWorkerOptions(betterproto.Message):
+ security: Dict[str, "ApiWorkerScopes"] = betterproto.map_field(
+ 1, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
+ )
+ """Apply security definitions to this operation"""
+
+ security_disabled: bool = betterproto.bool_field(2)
+ """
+ explicitly disable security for this endpoint We need to do this as the
+ default value of a repeated field is always empty so there is no way of
+ knowing if security is explicitly disabled
+ """
explicitly disable security for this endpoint We need to do this as the
+default value of a repeated field is always empty so there is no way of
+knowing if security is explicitly disabled
@dataclass(eq=False, repr=False)
+class ClientMessage(betterproto.Message):
+ """Messages the client is able to send to the server"""
+
+ id: str = betterproto.string_field(1)
+ """Client message ID, used to pair requests/responses"""
+
+ init_request: "InitRequest" = betterproto.message_field(2, group="content")
+ """
+ Client initialisation request A worker will not be eligible for triggers
+ until it has identified itself
+ """
+
+ trigger_response: "TriggerResponse" = betterproto.message_field(3, group="content")
+ """Client responsding with result of a trigger"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var id : str
+
+
Client message ID, used to pair requests/responses
Specific HttpResponse message Note this does not have to be handled by the
+User at all but they will have the option of control If they choose…
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class HttpResponseContext(betterproto.Message):
+ """
+ Specific HttpResponse message Note this does not have to be handled by the
+ User at all but they will have the option of control If they choose...
+ """
+
+ headers_old: Dict[str, str] = betterproto.map_field(
+ 1, betterproto.TYPE_STRING, betterproto.TYPE_STRING
+ )
+ """Old HTTP response headers (deprecated) TODO: Remove in 1.0"""
+
+ status: int = betterproto.int32_field(2)
+ """The HTTP status of the request"""
+
+ headers: Dict[str, "HeaderValue"] = betterproto.map_field(
+ 3, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
+ )
+ """HTTP response headers"""
+
+ def __post_init__(self) -> None:
+ super().__post_init__()
+ if self.is_set("headers_old"):
+ warnings.warn(
+ "HttpResponseContext.headers_old is deprecated", DeprecationWarning
+ )
InitRequest - Identifies a worker as ready to recieve triggers This message
+will contain information on the type of triggers that a worker is capable
+of handling
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class InitRequest(betterproto.Message):
+ """
+ InitRequest - Identifies a worker as ready to recieve triggers This message
+ will contain information on the type of triggers that a worker is capable
+ of handling
+ """
+
+ api: "ApiWorker" = betterproto.message_field(10, group="Worker")
+ subscription: "SubscriptionWorker" = betterproto.message_field(11, group="Worker")
+ schedule: "ScheduleWorker" = betterproto.message_field(12, group="Worker")
@dataclass(eq=False, repr=False)
+class ServerMessage(betterproto.Message):
+ """Messages the server is able to send to the client"""
+
+ id: str = betterproto.string_field(1)
+ """Server message ID, used to pair requests/responses"""
+
+ init_response: "InitResponse" = betterproto.message_field(2, group="content")
+ """
+ Server responding with client configuration details to an InitRequest
+ """
+
+ trigger_request: "TriggerRequest" = betterproto.message_field(3, group="content")
+ """Server requesting client to process a trigger"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var id : str
+
+
Server message ID, used to pair requests/responses
Specific event response message We do not accept responses for events only
+whether or not they were successfully processed
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class TopicResponseContext(betterproto.Message):
+ """
+ Specific event response message We do not accept responses for events only
+ whether or not they were successfully processed
+ """
+
+ success: bool = betterproto.bool_field(1)
+ """Success status of the handled event"""
@dataclass(eq=False, repr=False)
+class TriggerRequest(betterproto.Message):
+ """The server has a trigger for the client to handle"""
+
+ data: bytes = betterproto.bytes_field(1)
+ """The data in the trigger"""
+
+ mime_type: str = betterproto.string_field(2)
+ """Should we supply a mime type for the data? Or rely on context?"""
+
+ trace_context: "TraceContext" = betterproto.message_field(10)
+ """
+ TraceInformation from the membrane Allows tying traces from external
+ triggers (e.g. HttpRequests) into each event request/response pair of the
+ Bidirectional stream. which cannot be facilitated by OOTB stream
+ interceptors from OTEL.
+ """
+
+ http: "HttpTriggerContext" = betterproto.message_field(3, group="context")
+ topic: "TopicTriggerContext" = betterproto.message_field(4, group="context")
TraceInformation from the membrane Allows tying traces from external
+triggers (e.g. HttpRequests) into each event request/response pair of the
+Bidirectional stream. which cannot be facilitated by OOTB stream
+interceptors from OTEL.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/queue/index.html b/docs/nitric/proto/nitric/queue/index.html
new file mode 100644
index 0000000..de96adb
--- /dev/null
+++ b/docs/nitric/proto/nitric/queue/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.queue API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.queue
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class NitricTask(betterproto.Message):
+ """A task to be sent or received from a queue."""
+
+ id: str = betterproto.string_field(1)
+ """A unique id for the task"""
+
+ lease_id: str = betterproto.string_field(2)
+ """
+ The lease id unique to the pop request, this must be used to complete,
+ extend the lease or release the task.
+ """
+
+ payload_type: str = betterproto.string_field(3)
+ """A content hint for the tasks payload"""
+
+ payload: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(4)
+ """The payload of the task"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var id : str
+
+
A unique id for the task
+
+
var lease_id : str
+
+
The lease id unique to the pop request, this must be used to complete,
+extend the lease or release the task.
+
+
var payload : betterproto.lib.google.protobuf.Struct
@dataclass(eq=False, repr=False)
+class QueueCompleteRequest(betterproto.Message):
+ queue: str = betterproto.string_field(1)
+ """
+ The nitric name for the queue this will automatically be resolved to the
+ provider specific queue identifier.
+ """
+
+ lease_id: str = betterproto.string_field(2)
+ """Lease id of the task to be completed"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var lease_id : str
+
+
Lease id of the task to be completed
+
+
var queue : str
+
+
The nitric name for the queue
+this will automatically be resolved to the
+provider specific queue identifier.
@dataclass(eq=False, repr=False)
+class QueueReceiveRequest(betterproto.Message):
+ queue: str = betterproto.string_field(1)
+ """
+ The nitric name for the queue this will automatically be resolved to the
+ provider specific queue identifier.
+ """
+
+ depth: int = betterproto.int32_field(2)
+ """
+ The max number of items to pop off the queue, may be capped by provider
+ specific limitations
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var depth : int
+
+
The max number of items to pop off the queue, may be capped by provider
+specific limitations
+
+
var queue : str
+
+
The nitric name for the queue this will automatically be resolved to the
+provider specific queue identifier.
@dataclass(eq=False, repr=False)
+class QueueSendBatchRequest(betterproto.Message):
+ queue: str = betterproto.string_field(1)
+ """
+ The Nitric name for the queue this will automatically be resolved to the
+ provider specific queue identifier.
+ """
+
+ tasks: List["NitricTask"] = betterproto.message_field(2)
+ """Array of tasks to push to the queue"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var queue : str
+
+
The Nitric name for the queue this will automatically be resolved to the
+provider specific queue identifier.
@dataclass(eq=False, repr=False)
+class QueueSendBatchResponse(betterproto.Message):
+ """Response for sending a collection of tasks"""
+
+ failed_tasks: List["FailedTask"] = betterproto.message_field(1)
+ """A list of tasks that failed to be queued"""
@dataclass(eq=False, repr=False)
+class QueueSendRequest(betterproto.Message):
+ """Request to push a single event to a queue"""
+
+ queue: str = betterproto.string_field(1)
+ """
+ The Nitric name for the queue this will automatically be resolved to the
+ provider specific queue identifier.
+ """
+
+ task: "NitricTask" = betterproto.message_field(2)
+ """The task to push to the queue"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var queue : str
+
+
The Nitric name for the queue this will automatically be resolved to the
+provider specific queue identifier.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/resource/index.html b/docs/nitric/proto/nitric/resource/index.html
new file mode 100644
index 0000000..8619c30
--- /dev/null
+++ b/docs/nitric/proto/nitric/resource/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.resource API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.resource
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class ApiResource(betterproto.Message):
+ security_definitions: Dict[str, "ApiSecurityDefinition"] = betterproto.map_field(
+ 1, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
+ )
+ """
+ Security definitions for the api These may be used by registered routes and
+ operations on the API
+ """
+
+ security: Dict[str, "ApiScopes"] = betterproto.map_field(
+ 2, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
+ )
+ """root level security for this api"""
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/secret/index.html b/docs/nitric/proto/nitric/secret/index.html
new file mode 100644
index 0000000..e5c1d07
--- /dev/null
+++ b/docs/nitric/proto/nitric/secret/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.secret API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.secret
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class SecretAccessRequest(betterproto.Message):
+ """Request to get a secret from a Secret Store"""
+
+ secret_version: "SecretVersion" = betterproto.message_field(1)
+ """The id of the secret"""
@dataclass(eq=False, repr=False)
+class SecretAccessResponse(betterproto.Message):
+ """The secret response"""
+
+ secret_version: "SecretVersion" = betterproto.message_field(1)
+ """The version of the secret that was requested"""
+
+ value: bytes = betterproto.bytes_field(2)
+ """The value of the secret"""
@dataclass(eq=False, repr=False)
+class SecretPutRequest(betterproto.Message):
+ """Request to put a secret to a Secret Store"""
+
+ secret: "Secret" = betterproto.message_field(1)
+ """The Secret to put to the Secret store"""
+
+ value: bytes = betterproto.bytes_field(2)
+ """The value to assign to that secret"""
@dataclass(eq=False, repr=False)
+class SecretPutResponse(betterproto.Message):
+ """Result from putting the secret to a Secret Store"""
+
+ secret_version: "SecretVersion" = betterproto.message_field(1)
+ """The id of the secret"""
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/nitric/storage/index.html b/docs/nitric/proto/nitric/storage/index.html
new file mode 100644
index 0000000..d936a53
--- /dev/null
+++ b/docs/nitric/proto/nitric/storage/index.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+nitric.proto.nitric.storage API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.nitric.storage
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
@dataclass(eq=False, repr=False)
+class StorageListFilesResponse(betterproto.Message):
+ files: List["File"] = betterproto.message_field(1)
+ """keys of the files in the bucket"""
Request to generate a pre-signed URL for a file to perform a specific
+operation, such as read or write.
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class StoragePreSignUrlRequest(betterproto.Message):
+ """
+ Request to generate a pre-signed URL for a file to perform a specific
+ operation, such as read or write.
+ """
+
+ bucket_name: str = betterproto.string_field(1)
+ """
+ Nitric name of the bucket to retrieve from this will be automatically
+ resolved to the provider specific bucket identifier.
+ """
+
+ key: str = betterproto.string_field(2)
+ """
+ Key of item to generate the signed URL for. The URL and the token it
+ contains will only be valid for operations on this resource specifically.
+ """
+
+ operation: "StoragePreSignUrlRequestOperation" = betterproto.enum_field(3)
+ expiry: int = betterproto.uint32_field(4)
+ """
+ Expiry time in seconds for the token included in the signed URL. Time
+ starts from when the access token is generated, not when this request is
+ made. e.g. time.Now().Add(expiry * time.Second) on the server
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var bucket_name : str
+
+
Nitric name of the bucket to retrieve from
+this will be automatically
+resolved to the provider specific bucket identifier.
+
+
var expiry : int
+
+
Expiry time in seconds for the token included in the signed URL.
+Time
+starts from when the access token is generated, not when this request is
+made.
+e.g. time.Now().Add(expiry * time.Second) on the server
+
+
var key : str
+
+
Key of item to generate the signed URL for. The URL and the token it
+contains will only be valid for operations on this resource specifically.
@dataclass(eq=False, repr=False)
+class StoragePreSignUrlResponse(betterproto.Message):
+ url: str = betterproto.string_field(1)
+ """
+ The pre-signed url, restricted to the operation, resource and expiry time
+ specified in the request.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var url : str
+
+
The pre-signed url, restricted to the operation, resource and expiry time
+specified in the request.
@dataclass(eq=False, repr=False)
+class StorageReadRequest(betterproto.Message):
+ """Request to retrieve a storage item"""
+
+ bucket_name: str = betterproto.string_field(1)
+ """
+ Nitric name of the bucket to retrieve from this will be automatically
+ resolved to the provider specific bucket identifier.
+ """
+
+ key: str = betterproto.string_field(2)
+ """Key of item to retrieve"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var bucket_name : str
+
+
Nitric name of the bucket to retrieve from
+this will be automatically
+resolved to the provider specific bucket identifier.
@dataclass(eq=False, repr=False)
+class StorageWriteRequest(betterproto.Message):
+ """Request to put (create/update) a storage item"""
+
+ bucket_name: str = betterproto.string_field(1)
+ """
+ Nitric name of the bucket to store in this will be automatically resolved
+ to the provider specific bucket identifier.
+ """
+
+ key: str = betterproto.string_field(2)
+ """Key to store the item under"""
+
+ body: bytes = betterproto.bytes_field(3)
+ """bytes array to store"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var body : bytes
+
+
bytes array to store
+
+
var bucket_name : str
+
+
Nitric name of the bucket to store in
+this will be automatically resolved
+to the provider specific bucket identifier.
+
+
var key : str
+
+
Key to store the item under
+
+
+
+
+class StorageWriteResponse
+
+
+
Result of putting a storage item
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class StorageWriteResponse(betterproto.Message):
+ """Result of putting a storage item"""
+
+ pass
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/proto/validate/index.html b/docs/nitric/proto/validate/index.html
new file mode 100644
index 0000000..3802cd2
--- /dev/null
+++ b/docs/nitric/proto/validate/index.html
@@ -0,0 +1,4135 @@
+
+
+
+
+
+
+nitric.proto.validate API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.proto.validate
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# sources: validate/validate.proto
+# plugin: python-betterproto
+from dataclasses import dataclass
+from datetime import (
+ datetime,
+ timedelta,
+)
+from typing import List
+
+import betterproto
+
+
+class KnownRegex(betterproto.Enum):
+ """WellKnownRegex contain some well-known patterns."""
+
+ UNKNOWN = 0
+ HTTP_HEADER_NAME = 1
+ """HTTP header name as defined by RFC 7230."""
+
+ HTTP_HEADER_VALUE = 2
+ """HTTP header value as defined by RFC 7230."""
+
+
+@dataclass(eq=False, repr=False)
+class FieldRules(betterproto.Message):
+ """
+ FieldRules encapsulates the rules for each type of field. Depending on the
+ field, the correct set should be used to ensure proper validations.
+ """
+
+ message: "MessageRules" = betterproto.message_field(17)
+ float: "FloatRules" = betterproto.message_field(1, group="type")
+ """Scalar Field Types"""
+
+ double: "DoubleRules" = betterproto.message_field(2, group="type")
+ int32: "Int32Rules" = betterproto.message_field(3, group="type")
+ int64: "Int64Rules" = betterproto.message_field(4, group="type")
+ uint32: "UInt32Rules" = betterproto.message_field(5, group="type")
+ uint64: "UInt64Rules" = betterproto.message_field(6, group="type")
+ sint32: "SInt32Rules" = betterproto.message_field(7, group="type")
+ sint64: "SInt64Rules" = betterproto.message_field(8, group="type")
+ fixed32: "Fixed32Rules" = betterproto.message_field(9, group="type")
+ fixed64: "Fixed64Rules" = betterproto.message_field(10, group="type")
+ sfixed32: "SFixed32Rules" = betterproto.message_field(11, group="type")
+ sfixed64: "SFixed64Rules" = betterproto.message_field(12, group="type")
+ bool: "BoolRules" = betterproto.message_field(13, group="type")
+ string: "StringRules" = betterproto.message_field(14, group="type")
+ bytes: "BytesRules" = betterproto.message_field(15, group="type")
+ enum: "EnumRules" = betterproto.message_field(16, group="type")
+ """Complex Field Types"""
+
+ repeated: "RepeatedRules" = betterproto.message_field(18, group="type")
+ map: "MapRules" = betterproto.message_field(19, group="type")
+ any: "AnyRules" = betterproto.message_field(20, group="type")
+ """Well-Known Field Types"""
+
+ duration: "DurationRules" = betterproto.message_field(21, group="type")
+ timestamp: "TimestampRules" = betterproto.message_field(22, group="type")
+
+
+@dataclass(eq=False, repr=False)
+class FloatRules(betterproto.Message):
+ """FloatRules describes the constraints applied to `float` values"""
+
+ const: float = betterproto.float_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: float = betterproto.float_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: float = betterproto.float_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: float = betterproto.float_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: float = betterproto.float_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[float] = betterproto.float_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[float] = betterproto.float_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class DoubleRules(betterproto.Message):
+ """DoubleRules describes the constraints applied to `double` values"""
+
+ const: float = betterproto.double_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: float = betterproto.double_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: float = betterproto.double_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: float = betterproto.double_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: float = betterproto.double_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[float] = betterproto.double_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[float] = betterproto.double_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class Int32Rules(betterproto.Message):
+ """Int32Rules describes the constraints applied to `int32` values"""
+
+ const: int = betterproto.int32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.int32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.int32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.int32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.int32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.int32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class Int64Rules(betterproto.Message):
+ """Int64Rules describes the constraints applied to `int64` values"""
+
+ const: int = betterproto.int64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.int64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.int64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.int64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.int64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.int64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class UInt32Rules(betterproto.Message):
+ """UInt32Rules describes the constraints applied to `uint32` values"""
+
+ const: int = betterproto.uint32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.uint32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.uint32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.uint32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.uint32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.uint32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.uint32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class UInt64Rules(betterproto.Message):
+ """UInt64Rules describes the constraints applied to `uint64` values"""
+
+ const: int = betterproto.uint64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.uint64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.uint64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.uint64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.uint64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.uint64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.uint64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class SInt32Rules(betterproto.Message):
+ """SInt32Rules describes the constraints applied to `sint32` values"""
+
+ const: int = betterproto.sint32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sint32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sint32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sint32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sint32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sint32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sint32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class SInt64Rules(betterproto.Message):
+ """SInt64Rules describes the constraints applied to `sint64` values"""
+
+ const: int = betterproto.sint64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sint64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sint64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sint64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sint64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sint64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sint64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class Fixed32Rules(betterproto.Message):
+ """Fixed32Rules describes the constraints applied to `fixed32` values"""
+
+ const: int = betterproto.fixed32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.fixed32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.fixed32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.fixed32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.fixed32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.fixed32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.fixed32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class Fixed64Rules(betterproto.Message):
+ """Fixed64Rules describes the constraints applied to `fixed64` values"""
+
+ const: int = betterproto.fixed64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.fixed64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.fixed64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.fixed64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.fixed64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.fixed64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.fixed64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class SFixed32Rules(betterproto.Message):
+ """SFixed32Rules describes the constraints applied to `sfixed32` values"""
+
+ const: int = betterproto.sfixed32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sfixed32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sfixed32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sfixed32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sfixed32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sfixed32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sfixed32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class SFixed64Rules(betterproto.Message):
+ """SFixed64Rules describes the constraints applied to `sfixed64` values"""
+
+ const: int = betterproto.sfixed64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sfixed64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sfixed64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sfixed64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sfixed64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sfixed64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sfixed64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class BoolRules(betterproto.Message):
+ """BoolRules describes the constraints applied to `bool` values"""
+
+ const: bool = betterproto.bool_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+
+@dataclass(eq=False, repr=False)
+class StringRules(betterproto.Message):
+ """StringRules describe the constraints applied to `string` values"""
+
+ const: str = betterproto.string_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ len: int = betterproto.uint64_field(19)
+ """
+ Len specifies that this field must be the specified number of characters
+ (Unicode code points). Note that the number of characters may differ from
+ the number of bytes in the string.
+ """
+
+ min_len: int = betterproto.uint64_field(2)
+ """
+ MinLen specifies that this field must be the specified number of characters
+ (Unicode code points) at a minimum. Note that the number of characters may
+ differ from the number of bytes in the string.
+ """
+
+ max_len: int = betterproto.uint64_field(3)
+ """
+ MaxLen specifies that this field must be the specified number of characters
+ (Unicode code points) at a maximum. Note that the number of characters may
+ differ from the number of bytes in the string.
+ """
+
+ len_bytes: int = betterproto.uint64_field(20)
+ """
+ LenBytes specifies that this field must be the specified number of bytes at
+ a minimum
+ """
+
+ min_bytes: int = betterproto.uint64_field(4)
+ """
+ MinBytes specifies that this field must be the specified number of bytes at
+ a minimum
+ """
+
+ max_bytes: int = betterproto.uint64_field(5)
+ """
+ MaxBytes specifies that this field must be the specified number of bytes at
+ a maximum
+ """
+
+ pattern: str = betterproto.string_field(6)
+ """
+ Pattern specifes that this field must match against the specified regular
+ expression (RE2 syntax). The included expression should elide any
+ delimiters.
+ """
+
+ prefix: str = betterproto.string_field(7)
+ """
+ Prefix specifies that this field must have the specified substring at the
+ beginning of the string.
+ """
+
+ suffix: str = betterproto.string_field(8)
+ """
+ Suffix specifies that this field must have the specified substring at the
+ end of the string.
+ """
+
+ contains: str = betterproto.string_field(9)
+ """
+ Contains specifies that this field must have the specified substring
+ anywhere in the string.
+ """
+
+ not_contains: str = betterproto.string_field(23)
+ """
+ NotContains specifies that this field cannot have the specified substring
+ anywhere in the string.
+ """
+
+ in_: List[str] = betterproto.string_field(10)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[str] = betterproto.string_field(11)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ email: bool = betterproto.bool_field(12, group="well_known")
+ """
+ Email specifies that the field must be a valid email address as defined by
+ RFC 5322
+ """
+
+ hostname: bool = betterproto.bool_field(13, group="well_known")
+ """
+ Hostname specifies that the field must be a valid hostname as defined by
+ RFC 1034. This constraint does not support internationalized domain names
+ (IDNs).
+ """
+
+ ip: bool = betterproto.bool_field(14, group="well_known")
+ """
+ Ip specifies that the field must be a valid IP (v4 or v6) address. Valid
+ IPv6 addresses should not include surrounding square brackets.
+ """
+
+ ipv4: bool = betterproto.bool_field(15, group="well_known")
+ """Ipv4 specifies that the field must be a valid IPv4 address."""
+
+ ipv6: bool = betterproto.bool_field(16, group="well_known")
+ """
+ Ipv6 specifies that the field must be a valid IPv6 address. Valid IPv6
+ addresses should not include surrounding square brackets.
+ """
+
+ uri: bool = betterproto.bool_field(17, group="well_known")
+ """
+ Uri specifies that the field must be a valid, absolute URI as defined by
+ RFC 3986
+ """
+
+ uri_ref: bool = betterproto.bool_field(18, group="well_known")
+ """
+ UriRef specifies that the field must be a valid URI as defined by RFC 3986
+ and may be relative or absolute.
+ """
+
+ address: bool = betterproto.bool_field(21, group="well_known")
+ """
+ Address specifies that the field must be either a valid hostname as defined
+ by RFC 1034 (which does not support internationalized domain names or
+ IDNs), or it can be a valid IP (v4 or v6).
+ """
+
+ uuid: bool = betterproto.bool_field(22, group="well_known")
+ """
+ Uuid specifies that the field must be a valid UUID as defined by RFC 4122
+ """
+
+ well_known_regex: "KnownRegex" = betterproto.enum_field(24, group="well_known")
+ """
+ WellKnownRegex specifies a common well known pattern defined as a regex.
+ """
+
+ strict: bool = betterproto.bool_field(25)
+ """
+ This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable
+ strict header validation. By default, this is true, and HTTP header
+ validations are RFC-compliant. Setting to false will enable a looser
+ validations that only disallows \r\n\0 characters, which can be used to
+ bypass header matching rules.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(26)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class BytesRules(betterproto.Message):
+ """BytesRules describe the constraints applied to `bytes` values"""
+
+ const: bytes = betterproto.bytes_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ len: int = betterproto.uint64_field(13)
+ """Len specifies that this field must be the specified number of bytes"""
+
+ min_len: int = betterproto.uint64_field(2)
+ """
+ MinLen specifies that this field must be the specified number of bytes at a
+ minimum
+ """
+
+ max_len: int = betterproto.uint64_field(3)
+ """
+ MaxLen specifies that this field must be the specified number of bytes at a
+ maximum
+ """
+
+ pattern: str = betterproto.string_field(4)
+ """
+ Pattern specifes that this field must match against the specified regular
+ expression (RE2 syntax). The included expression should elide any
+ delimiters.
+ """
+
+ prefix: bytes = betterproto.bytes_field(5)
+ """
+ Prefix specifies that this field must have the specified bytes at the
+ beginning of the string.
+ """
+
+ suffix: bytes = betterproto.bytes_field(6)
+ """
+ Suffix specifies that this field must have the specified bytes at the end
+ of the string.
+ """
+
+ contains: bytes = betterproto.bytes_field(7)
+ """
+ Contains specifies that this field must have the specified bytes anywhere
+ in the string.
+ """
+
+ in_: List[bytes] = betterproto.bytes_field(8)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[bytes] = betterproto.bytes_field(9)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ip: bool = betterproto.bool_field(10, group="well_known")
+ """
+ Ip specifies that the field must be a valid IP (v4 or v6) address in byte
+ format
+ """
+
+ ipv4: bool = betterproto.bool_field(11, group="well_known")
+ """
+ Ipv4 specifies that the field must be a valid IPv4 address in byte format
+ """
+
+ ipv6: bool = betterproto.bool_field(12, group="well_known")
+ """
+ Ipv6 specifies that the field must be a valid IPv6 address in byte format
+ """
+
+ ignore_empty: bool = betterproto.bool_field(14)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class EnumRules(betterproto.Message):
+ """EnumRules describe the constraints applied to enum values"""
+
+ const: int = betterproto.int32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ defined_only: bool = betterproto.bool_field(2)
+ """
+ DefinedOnly specifies that this field must be only one of the defined
+ values for this enum, failing on any undefined value.
+ """
+
+ in_: List[int] = betterproto.int32_field(3)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int32_field(4)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+
+@dataclass(eq=False, repr=False)
+class MessageRules(betterproto.Message):
+ """
+ MessageRules describe the constraints applied to embedded message values.
+ For message-type fields, validation is performed recursively.
+ """
+
+ skip: bool = betterproto.bool_field(1)
+ """
+ Skip specifies that the validation rules of this field should not be
+ evaluated
+ """
+
+ required: bool = betterproto.bool_field(2)
+ """Required specifies that this field must be set"""
+
+
+@dataclass(eq=False, repr=False)
+class RepeatedRules(betterproto.Message):
+ """RepeatedRules describe the constraints applied to `repeated` values"""
+
+ min_items: int = betterproto.uint64_field(1)
+ """
+ MinItems specifies that this field must have the specified number of items
+ at a minimum
+ """
+
+ max_items: int = betterproto.uint64_field(2)
+ """
+ MaxItems specifies that this field must have the specified number of items
+ at a maximum
+ """
+
+ unique: bool = betterproto.bool_field(3)
+ """
+ Unique specifies that all elements in this field must be unique. This
+ contraint is only applicable to scalar and enum types (messages are not
+ supported).
+ """
+
+ items: "FieldRules" = betterproto.message_field(4)
+ """
+ Items specifies the contraints to be applied to each item in the field.
+ Repeated message fields will still execute validation against each item
+ unless skip is specified here.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(5)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class MapRules(betterproto.Message):
+ """MapRules describe the constraints applied to `map` values"""
+
+ min_pairs: int = betterproto.uint64_field(1)
+ """
+ MinPairs specifies that this field must have the specified number of KVs at
+ a minimum
+ """
+
+ max_pairs: int = betterproto.uint64_field(2)
+ """
+ MaxPairs specifies that this field must have the specified number of KVs at
+ a maximum
+ """
+
+ no_sparse: bool = betterproto.bool_field(3)
+ """
+ NoSparse specifies values in this field cannot be unset. This only applies
+ to map's with message value types.
+ """
+
+ keys: "FieldRules" = betterproto.message_field(4)
+ """
+ Keys specifies the constraints to be applied to each key in the field.
+ """
+
+ values: "FieldRules" = betterproto.message_field(5)
+ """
+ Values specifies the constraints to be applied to the value of each key in
+ the field. Message values will still have their validations evaluated
+ unless skip is specified here.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(6)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
+@dataclass(eq=False, repr=False)
+class AnyRules(betterproto.Message):
+ """
+ AnyRules describe constraints applied exclusively to the
+ `google.protobuf.Any` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ in_: List[str] = betterproto.string_field(2)
+ """
+ In specifies that this field's `type_url` must be equal to one of the
+ specified values.
+ """
+
+ not_in: List[str] = betterproto.string_field(3)
+ """
+ NotIn specifies that this field's `type_url` must not be equal to any of
+ the specified values.
+ """
+
+
+@dataclass(eq=False, repr=False)
+class DurationRules(betterproto.Message):
+ """
+ DurationRules describe the constraints applied exclusively to the
+ `google.protobuf.Duration` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ const: timedelta = betterproto.message_field(2)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: timedelta = betterproto.message_field(3)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: timedelta = betterproto.message_field(4)
+ """
+ Lt specifies that this field must be less than the specified value,
+ inclusive
+ """
+
+ gt: timedelta = betterproto.message_field(5)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive
+ """
+
+ gte: timedelta = betterproto.message_field(6)
+ """
+ Gte specifies that this field must be greater than the specified value,
+ inclusive
+ """
+
+ in_: List[timedelta] = betterproto.message_field(7)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[timedelta] = betterproto.message_field(8)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+
+@dataclass(eq=False, repr=False)
+class TimestampRules(betterproto.Message):
+ """
+ TimestampRules describe the constraints applied exclusively to the
+ `google.protobuf.Timestamp` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ const: datetime = betterproto.message_field(2)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: datetime = betterproto.message_field(3)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: datetime = betterproto.message_field(4)
+ """
+ Lte specifies that this field must be less than the specified value,
+ inclusive
+ """
+
+ gt: datetime = betterproto.message_field(5)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive
+ """
+
+ gte: datetime = betterproto.message_field(6)
+ """
+ Gte specifies that this field must be greater than the specified value,
+ inclusive
+ """
+
+ lt_now: bool = betterproto.bool_field(7)
+ """
+ LtNow specifies that this must be less than the current time. LtNow can
+ only be used with the Within rule.
+ """
+
+ gt_now: bool = betterproto.bool_field(8)
+ """
+ GtNow specifies that this must be greater than the current time. GtNow can
+ only be used with the Within rule.
+ """
+
+ within: timedelta = betterproto.message_field(9)
+ """
+ Within specifies that this field must be within this duration of the
+ current time. This constraint can be used alone or with the LtNow and GtNow
+ rules.
+ """
AnyRules describe constraints applied exclusively to the
+google.protobuf.Any well-known type
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class AnyRules(betterproto.Message):
+ """
+ AnyRules describe constraints applied exclusively to the
+ `google.protobuf.Any` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ in_: List[str] = betterproto.string_field(2)
+ """
+ In specifies that this field's `type_url` must be equal to one of the
+ specified values.
+ """
+
+ not_in: List[str] = betterproto.string_field(3)
+ """
+ NotIn specifies that this field's `type_url` must not be equal to any of
+ the specified values.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var in_ : List[str]
+
+
In specifies that this field's type_url must be equal to one of the
+specified values.
+
+
var not_in : List[str]
+
+
NotIn specifies that this field's type_url must not be equal to any of
+the specified values.
BoolRules describes the constraints applied to bool values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class BoolRules(betterproto.Message):
+ """BoolRules describes the constraints applied to `bool` values"""
+
+ const: bool = betterproto.bool_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : bool
+
+
Const specifies that this field must be exactly the specified value
BytesRules describe the constraints applied to bytes values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class BytesRules(betterproto.Message):
+ """BytesRules describe the constraints applied to `bytes` values"""
+
+ const: bytes = betterproto.bytes_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ len: int = betterproto.uint64_field(13)
+ """Len specifies that this field must be the specified number of bytes"""
+
+ min_len: int = betterproto.uint64_field(2)
+ """
+ MinLen specifies that this field must be the specified number of bytes at a
+ minimum
+ """
+
+ max_len: int = betterproto.uint64_field(3)
+ """
+ MaxLen specifies that this field must be the specified number of bytes at a
+ maximum
+ """
+
+ pattern: str = betterproto.string_field(4)
+ """
+ Pattern specifes that this field must match against the specified regular
+ expression (RE2 syntax). The included expression should elide any
+ delimiters.
+ """
+
+ prefix: bytes = betterproto.bytes_field(5)
+ """
+ Prefix specifies that this field must have the specified bytes at the
+ beginning of the string.
+ """
+
+ suffix: bytes = betterproto.bytes_field(6)
+ """
+ Suffix specifies that this field must have the specified bytes at the end
+ of the string.
+ """
+
+ contains: bytes = betterproto.bytes_field(7)
+ """
+ Contains specifies that this field must have the specified bytes anywhere
+ in the string.
+ """
+
+ in_: List[bytes] = betterproto.bytes_field(8)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[bytes] = betterproto.bytes_field(9)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ip: bool = betterproto.bool_field(10, group="well_known")
+ """
+ Ip specifies that the field must be a valid IP (v4 or v6) address in byte
+ format
+ """
+
+ ipv4: bool = betterproto.bool_field(11, group="well_known")
+ """
+ Ipv4 specifies that the field must be a valid IPv4 address in byte format
+ """
+
+ ipv6: bool = betterproto.bool_field(12, group="well_known")
+ """
+ Ipv6 specifies that the field must be a valid IPv6 address in byte format
+ """
+
+ ignore_empty: bool = betterproto.bool_field(14)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : bytes
+
+
Const specifies that this field must be exactly the specified value
+
+
var contains : bytes
+
+
Contains specifies that this field must have the specified bytes anywhere
+in the string.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[bytes]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var ip : bool
+
+
Ip specifies that the field must be a valid IP (v4 or v6) address in byte
+format
+
+
var ipv4 : bool
+
+
Ipv4 specifies that the field must be a valid IPv4 address in byte format
+
+
var ipv6 : bool
+
+
Ipv6 specifies that the field must be a valid IPv6 address in byte format
+
+
var len : int
+
+
Len specifies that this field must be the specified number of bytes
+
+
var max_len : int
+
+
MaxLen specifies that this field must be the specified number of bytes at a
+maximum
+
+
var min_len : int
+
+
MinLen specifies that this field must be the specified number of bytes at a
+minimum
+
+
var not_in : List[bytes]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
var pattern : str
+
+
Pattern specifes that this field must match against the specified regular
+expression (RE2 syntax). The included expression should elide any
+delimiters.
+
+
var prefix : bytes
+
+
Prefix specifies that this field must have the specified bytes at the
+beginning of the string.
+
+
var suffix : bytes
+
+
Suffix specifies that this field must have the specified bytes at the end
+of the string.
DoubleRules describes the constraints applied to double values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class DoubleRules(betterproto.Message):
+ """DoubleRules describes the constraints applied to `double` values"""
+
+ const: float = betterproto.double_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: float = betterproto.double_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: float = betterproto.double_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: float = betterproto.double_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: float = betterproto.double_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[float] = betterproto.double_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[float] = betterproto.double_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : float
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : float
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : float
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[float]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : float
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : float
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[float]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
DurationRules describe the constraints applied exclusively to the
+google.protobuf.Duration well-known type
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class DurationRules(betterproto.Message):
+ """
+ DurationRules describe the constraints applied exclusively to the
+ `google.protobuf.Duration` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ const: timedelta = betterproto.message_field(2)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: timedelta = betterproto.message_field(3)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: timedelta = betterproto.message_field(4)
+ """
+ Lt specifies that this field must be less than the specified value,
+ inclusive
+ """
+
+ gt: timedelta = betterproto.message_field(5)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive
+ """
+
+ gte: timedelta = betterproto.message_field(6)
+ """
+ Gte specifies that this field must be greater than the specified value,
+ inclusive
+ """
+
+ in_: List[timedelta] = betterproto.message_field(7)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[timedelta] = betterproto.message_field(8)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : datetime.timedelta
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : datetime.timedelta
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive
+
+
var gte : datetime.timedelta
+
+
Gte specifies that this field must be greater than the specified value,
+inclusive
+
+
var in_ : List[datetime.timedelta]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : datetime.timedelta
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : datetime.timedelta
+
+
Lt specifies that this field must be less than the specified value,
+inclusive
+
+
var not_in : List[datetime.timedelta]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
EnumRules describe the constraints applied to enum values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class EnumRules(betterproto.Message):
+ """EnumRules describe the constraints applied to enum values"""
+
+ const: int = betterproto.int32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ defined_only: bool = betterproto.bool_field(2)
+ """
+ DefinedOnly specifies that this field must be only one of the defined
+ values for this enum, failing on any undefined value.
+ """
+
+ in_: List[int] = betterproto.int32_field(3)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int32_field(4)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var defined_only : bool
+
+
DefinedOnly specifies that this field must be only one of the defined
+values for this enum, failing on any undefined value.
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+class Fixed32Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
Fixed32Rules describes the constraints applied to fixed32 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Fixed32Rules(betterproto.Message):
+ """Fixed32Rules describes the constraints applied to `fixed32` values"""
+
+ const: int = betterproto.fixed32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.fixed32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.fixed32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.fixed32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.fixed32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.fixed32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.fixed32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class Fixed64Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
Fixed64Rules describes the constraints applied to fixed64 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Fixed64Rules(betterproto.Message):
+ """Fixed64Rules describes the constraints applied to `fixed64` values"""
+
+ const: int = betterproto.fixed64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.fixed64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.fixed64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.fixed64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.fixed64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.fixed64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.fixed64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
FloatRules describes the constraints applied to float values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class FloatRules(betterproto.Message):
+ """FloatRules describes the constraints applied to `float` values"""
+
+ const: float = betterproto.float_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: float = betterproto.float_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: float = betterproto.float_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: float = betterproto.float_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: float = betterproto.float_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[float] = betterproto.float_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[float] = betterproto.float_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : float
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : float
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : float
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[float]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : float
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : float
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[float]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class Int32Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
Int32Rules describes the constraints applied to int32 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Int32Rules(betterproto.Message):
+ """Int32Rules describes the constraints applied to `int32` values"""
+
+ const: int = betterproto.int32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.int32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.int32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.int32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.int32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.int32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class Int64Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
Int64Rules describes the constraints applied to int64 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class Int64Rules(betterproto.Message):
+ """Int64Rules describes the constraints applied to `int64` values"""
+
+ const: int = betterproto.int64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.int64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.int64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.int64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.int64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.int64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.int64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
class KnownRegex(betterproto.Enum):
+ """WellKnownRegex contain some well-known patterns."""
+
+ UNKNOWN = 0
+ HTTP_HEADER_NAME = 1
+ """HTTP header name as defined by RFC 7230."""
+
+ HTTP_HEADER_VALUE = 2
+ """HTTP header value as defined by RFC 7230."""
MapRules describe the constraints applied to map values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class MapRules(betterproto.Message):
+ """MapRules describe the constraints applied to `map` values"""
+
+ min_pairs: int = betterproto.uint64_field(1)
+ """
+ MinPairs specifies that this field must have the specified number of KVs at
+ a minimum
+ """
+
+ max_pairs: int = betterproto.uint64_field(2)
+ """
+ MaxPairs specifies that this field must have the specified number of KVs at
+ a maximum
+ """
+
+ no_sparse: bool = betterproto.bool_field(3)
+ """
+ NoSparse specifies values in this field cannot be unset. This only applies
+ to map's with message value types.
+ """
+
+ keys: "FieldRules" = betterproto.message_field(4)
+ """
+ Keys specifies the constraints to be applied to each key in the field.
+ """
+
+ values: "FieldRules" = betterproto.message_field(5)
+ """
+ Values specifies the constraints to be applied to the value of each key in
+ the field. Message values will still have their validations evaluated
+ unless skip is specified here.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(6)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
Values specifies the constraints to be applied to the value of each key in
+the field. Message values will still have their validations evaluated
+unless skip is specified here.
MessageRules describe the constraints applied to embedded message values.
+For message-type fields, validation is performed recursively.
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class MessageRules(betterproto.Message):
+ """
+ MessageRules describe the constraints applied to embedded message values.
+ For message-type fields, validation is performed recursively.
+ """
+
+ skip: bool = betterproto.bool_field(1)
+ """
+ Skip specifies that the validation rules of this field should not be
+ evaluated
+ """
+
+ required: bool = betterproto.bool_field(2)
+ """Required specifies that this field must be set"""
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var required : bool
+
+
Required specifies that this field must be set
+
+
var skip : bool
+
+
Skip specifies that the validation rules of this field should not be
+evaluated
RepeatedRules describe the constraints applied to repeated values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class RepeatedRules(betterproto.Message):
+ """RepeatedRules describe the constraints applied to `repeated` values"""
+
+ min_items: int = betterproto.uint64_field(1)
+ """
+ MinItems specifies that this field must have the specified number of items
+ at a minimum
+ """
+
+ max_items: int = betterproto.uint64_field(2)
+ """
+ MaxItems specifies that this field must have the specified number of items
+ at a maximum
+ """
+
+ unique: bool = betterproto.bool_field(3)
+ """
+ Unique specifies that all elements in this field must be unique. This
+ contraint is only applicable to scalar and enum types (messages are not
+ supported).
+ """
+
+ items: "FieldRules" = betterproto.message_field(4)
+ """
+ Items specifies the contraints to be applied to each item in the field.
+ Repeated message fields will still execute validation against each item
+ unless skip is specified here.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(5)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
Items specifies the contraints to be applied to each item in the field.
+Repeated message fields will still execute validation against each item
+unless skip is specified here.
+
+
var max_items : int
+
+
MaxItems specifies that this field must have the specified number of items
+at a maximum
+
+
var min_items : int
+
+
MinItems specifies that this field must have the specified number of items
+at a minimum
+
+
var unique : bool
+
+
Unique specifies that all elements in this field must be unique. This
+contraint is only applicable to scalar and enum types (messages are not
+supported).
+
+
+
+
+class SFixed32Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
SFixed32Rules describes the constraints applied to sfixed32 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class SFixed32Rules(betterproto.Message):
+ """SFixed32Rules describes the constraints applied to `sfixed32` values"""
+
+ const: int = betterproto.sfixed32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sfixed32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sfixed32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sfixed32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sfixed32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sfixed32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sfixed32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class SFixed64Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
SFixed64Rules describes the constraints applied to sfixed64 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class SFixed64Rules(betterproto.Message):
+ """SFixed64Rules describes the constraints applied to `sfixed64` values"""
+
+ const: int = betterproto.sfixed64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sfixed64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sfixed64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sfixed64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sfixed64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sfixed64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sfixed64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class SInt32Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
SInt32Rules describes the constraints applied to sint32 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class SInt32Rules(betterproto.Message):
+ """SInt32Rules describes the constraints applied to `sint32` values"""
+
+ const: int = betterproto.sint32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sint32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sint32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sint32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sint32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sint32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sint32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class SInt64Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
SInt64Rules describes the constraints applied to sint64 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class SInt64Rules(betterproto.Message):
+ """SInt64Rules describes the constraints applied to `sint64` values"""
+
+ const: int = betterproto.sint64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.sint64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.sint64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.sint64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.sint64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.sint64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.sint64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
StringRules describe the constraints applied to string values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class StringRules(betterproto.Message):
+ """StringRules describe the constraints applied to `string` values"""
+
+ const: str = betterproto.string_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ len: int = betterproto.uint64_field(19)
+ """
+ Len specifies that this field must be the specified number of characters
+ (Unicode code points). Note that the number of characters may differ from
+ the number of bytes in the string.
+ """
+
+ min_len: int = betterproto.uint64_field(2)
+ """
+ MinLen specifies that this field must be the specified number of characters
+ (Unicode code points) at a minimum. Note that the number of characters may
+ differ from the number of bytes in the string.
+ """
+
+ max_len: int = betterproto.uint64_field(3)
+ """
+ MaxLen specifies that this field must be the specified number of characters
+ (Unicode code points) at a maximum. Note that the number of characters may
+ differ from the number of bytes in the string.
+ """
+
+ len_bytes: int = betterproto.uint64_field(20)
+ """
+ LenBytes specifies that this field must be the specified number of bytes at
+ a minimum
+ """
+
+ min_bytes: int = betterproto.uint64_field(4)
+ """
+ MinBytes specifies that this field must be the specified number of bytes at
+ a minimum
+ """
+
+ max_bytes: int = betterproto.uint64_field(5)
+ """
+ MaxBytes specifies that this field must be the specified number of bytes at
+ a maximum
+ """
+
+ pattern: str = betterproto.string_field(6)
+ """
+ Pattern specifes that this field must match against the specified regular
+ expression (RE2 syntax). The included expression should elide any
+ delimiters.
+ """
+
+ prefix: str = betterproto.string_field(7)
+ """
+ Prefix specifies that this field must have the specified substring at the
+ beginning of the string.
+ """
+
+ suffix: str = betterproto.string_field(8)
+ """
+ Suffix specifies that this field must have the specified substring at the
+ end of the string.
+ """
+
+ contains: str = betterproto.string_field(9)
+ """
+ Contains specifies that this field must have the specified substring
+ anywhere in the string.
+ """
+
+ not_contains: str = betterproto.string_field(23)
+ """
+ NotContains specifies that this field cannot have the specified substring
+ anywhere in the string.
+ """
+
+ in_: List[str] = betterproto.string_field(10)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[str] = betterproto.string_field(11)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ email: bool = betterproto.bool_field(12, group="well_known")
+ """
+ Email specifies that the field must be a valid email address as defined by
+ RFC 5322
+ """
+
+ hostname: bool = betterproto.bool_field(13, group="well_known")
+ """
+ Hostname specifies that the field must be a valid hostname as defined by
+ RFC 1034. This constraint does not support internationalized domain names
+ (IDNs).
+ """
+
+ ip: bool = betterproto.bool_field(14, group="well_known")
+ """
+ Ip specifies that the field must be a valid IP (v4 or v6) address. Valid
+ IPv6 addresses should not include surrounding square brackets.
+ """
+
+ ipv4: bool = betterproto.bool_field(15, group="well_known")
+ """Ipv4 specifies that the field must be a valid IPv4 address."""
+
+ ipv6: bool = betterproto.bool_field(16, group="well_known")
+ """
+ Ipv6 specifies that the field must be a valid IPv6 address. Valid IPv6
+ addresses should not include surrounding square brackets.
+ """
+
+ uri: bool = betterproto.bool_field(17, group="well_known")
+ """
+ Uri specifies that the field must be a valid, absolute URI as defined by
+ RFC 3986
+ """
+
+ uri_ref: bool = betterproto.bool_field(18, group="well_known")
+ """
+ UriRef specifies that the field must be a valid URI as defined by RFC 3986
+ and may be relative or absolute.
+ """
+
+ address: bool = betterproto.bool_field(21, group="well_known")
+ """
+ Address specifies that the field must be either a valid hostname as defined
+ by RFC 1034 (which does not support internationalized domain names or
+ IDNs), or it can be a valid IP (v4 or v6).
+ """
+
+ uuid: bool = betterproto.bool_field(22, group="well_known")
+ """
+ Uuid specifies that the field must be a valid UUID as defined by RFC 4122
+ """
+
+ well_known_regex: "KnownRegex" = betterproto.enum_field(24, group="well_known")
+ """
+ WellKnownRegex specifies a common well known pattern defined as a regex.
+ """
+
+ strict: bool = betterproto.bool_field(25)
+ """
+ This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable
+ strict header validation. By default, this is true, and HTTP header
+ validations are RFC-compliant. Setting to false will enable a looser
+ validations that only disallows \r\n\0 characters, which can be used to
+ bypass header matching rules.
+ """
+
+ ignore_empty: bool = betterproto.bool_field(26)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var address : bool
+
+
Address specifies that the field must be either a valid hostname as defined
+by RFC 1034 (which does not support internationalized domain names or
+IDNs), or it can be a valid IP (v4 or v6).
+
+
var const : str
+
+
Const specifies that this field must be exactly the specified value
+
+
var contains : str
+
+
Contains specifies that this field must have the specified substring
+anywhere in the string.
+
+
var email : bool
+
+
Email specifies that the field must be a valid email address as defined by
+RFC 5322
+
+
var hostname : bool
+
+
Hostname specifies that the field must be a valid hostname as defined by
+RFC 1034. This constraint does not support internationalized domain names
+(IDNs).
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[str]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var ip : bool
+
+
Ip specifies that the field must be a valid IP (v4 or v6) address. Valid
+IPv6 addresses should not include surrounding square brackets.
+
+
var ipv4 : bool
+
+
Ipv4 specifies that the field must be a valid IPv4 address.
+
+
var ipv6 : bool
+
+
Ipv6 specifies that the field must be a valid IPv6 address. Valid IPv6
+addresses should not include surrounding square brackets.
+
+
var len : int
+
+
Len specifies that this field must be the specified number of characters
+(Unicode code points). Note that the number of characters may differ from
+the number of bytes in the string.
+
+
var len_bytes : int
+
+
LenBytes specifies that this field must be the specified number of bytes at
+a minimum
+
+
var max_bytes : int
+
+
MaxBytes specifies that this field must be the specified number of bytes at
+a maximum
+
+
var max_len : int
+
+
MaxLen specifies that this field must be the specified number of characters
+(Unicode code points) at a maximum. Note that the number of characters may
+differ from the number of bytes in the string.
+
+
var min_bytes : int
+
+
MinBytes specifies that this field must be the specified number of bytes at
+a minimum
+
+
var min_len : int
+
+
MinLen specifies that this field must be the specified number of characters
+(Unicode code points) at a minimum. Note that the number of characters may
+differ from the number of bytes in the string.
+
+
var not_contains : str
+
+
NotContains specifies that this field cannot have the specified substring
+anywhere in the string.
+
+
var not_in : List[str]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
var pattern : str
+
+
Pattern specifes that this field must match against the specified regular
+expression (RE2 syntax). The included expression should elide any
+delimiters.
+
+
var prefix : str
+
+
Prefix specifies that this field must have the specified substring at the
+beginning of the string.
+
+
var strict : bool
+
+
This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable
+strict header validation. By default, this is true, and HTTP header
+validations are RFC-compliant. Setting to false will enable a looser
+validations that only disallows
+ characters, which can be used to
+bypass header matching rules.
+
+
var suffix : str
+
+
Suffix specifies that this field must have the specified substring at the
+end of the string.
+
+
var uri : bool
+
+
Uri specifies that the field must be a valid, absolute URI as defined by
+RFC 3986
+
+
var uri_ref : bool
+
+
UriRef specifies that the field must be a valid URI as defined by RFC 3986
+and may be relative or absolute.
+
+
var uuid : bool
+
+
Uuid specifies that the field must be a valid UUID as defined by RFC 4122
TimestampRules describe the constraints applied exclusively to the
+google.protobuf.Timestamp well-known type
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class TimestampRules(betterproto.Message):
+ """
+ TimestampRules describe the constraints applied exclusively to the
+ `google.protobuf.Timestamp` well-known type
+ """
+
+ required: bool = betterproto.bool_field(1)
+ """Required specifies that this field must be set"""
+
+ const: datetime = betterproto.message_field(2)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: datetime = betterproto.message_field(3)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: datetime = betterproto.message_field(4)
+ """
+ Lte specifies that this field must be less than the specified value,
+ inclusive
+ """
+
+ gt: datetime = betterproto.message_field(5)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive
+ """
+
+ gte: datetime = betterproto.message_field(6)
+ """
+ Gte specifies that this field must be greater than the specified value,
+ inclusive
+ """
+
+ lt_now: bool = betterproto.bool_field(7)
+ """
+ LtNow specifies that this must be less than the current time. LtNow can
+ only be used with the Within rule.
+ """
+
+ gt_now: bool = betterproto.bool_field(8)
+ """
+ GtNow specifies that this must be greater than the current time. GtNow can
+ only be used with the Within rule.
+ """
+
+ within: timedelta = betterproto.message_field(9)
+ """
+ Within specifies that this field must be within this duration of the
+ current time. This constraint can be used alone or with the LtNow and GtNow
+ rules.
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : datetime.datetime
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : datetime.datetime
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive
+
+
var gt_now : bool
+
+
GtNow specifies that this must be greater than the current time. GtNow can
+only be used with the Within rule.
+
+
var gte : datetime.datetime
+
+
Gte specifies that this field must be greater than the specified value,
+inclusive
+
+
var lt : datetime.datetime
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lt_now : bool
+
+
LtNow specifies that this must be less than the current time. LtNow can
+only be used with the Within rule.
+
+
var lte : datetime.datetime
+
+
Lte specifies that this field must be less than the specified value,
+inclusive
+
+
var required : bool
+
+
Required specifies that this field must be set
+
+
var within : datetime.timedelta
+
+
Within specifies that this field must be within this duration of the
+current time. This constraint can be used alone or with the LtNow and GtNow
+rules.
+
+
+
+
+class UInt32Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
UInt32Rules describes the constraints applied to uint32 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class UInt32Rules(betterproto.Message):
+ """UInt32Rules describes the constraints applied to `uint32` values"""
+
+ const: int = betterproto.uint32_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.uint32_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.uint32_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.uint32_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.uint32_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.uint32_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.uint32_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+class UInt64Rules
+(const: int = <object object>, lt: int = <object object>, lte: int = <object object>, gt: int = <object object>, gte: int = <object object>, in_: List[int] = <object object>, not_in: List[int] = <object object>, ignore_empty: bool = <object object>)
+
+
+
UInt64Rules describes the constraints applied to uint64 values
+
+
+Expand source code
+
+
@dataclass(eq=False, repr=False)
+class UInt64Rules(betterproto.Message):
+ """UInt64Rules describes the constraints applied to `uint64` values"""
+
+ const: int = betterproto.uint64_field(1)
+ """Const specifies that this field must be exactly the specified value"""
+
+ lt: int = betterproto.uint64_field(2)
+ """
+ Lt specifies that this field must be less than the specified value,
+ exclusive
+ """
+
+ lte: int = betterproto.uint64_field(3)
+ """
+ Lte specifies that this field must be less than or equal to the specified
+ value, inclusive
+ """
+
+ gt: int = betterproto.uint64_field(4)
+ """
+ Gt specifies that this field must be greater than the specified value,
+ exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+ range is reversed.
+ """
+
+ gte: int = betterproto.uint64_field(5)
+ """
+ Gte specifies that this field must be greater than or equal to the
+ specified value, inclusive. If the value of Gte is larger than a specified
+ Lt or Lte, the range is reversed.
+ """
+
+ in_: List[int] = betterproto.uint64_field(6)
+ """
+ In specifies that this field must be equal to one of the specified values
+ """
+
+ not_in: List[int] = betterproto.uint64_field(7)
+ """
+ NotIn specifies that this field cannot be equal to one of the specified
+ values
+ """
+
+ ignore_empty: bool = betterproto.bool_field(8)
+ """
+ IgnoreEmpty specifies that the validation rules of this field should be
+ evaluated only if the field is not empty
+ """
+
+
Ancestors
+
+
betterproto.Message
+
abc.ABC
+
+
Class variables
+
+
var const : int
+
+
Const specifies that this field must be exactly the specified value
+
+
var gt : int
+
+
Gt specifies that this field must be greater than the specified value,
+exclusive. If the value of Gt is larger than a specified Lt or Lte, the
+range is reversed.
+
+
var gte : int
+
+
Gte specifies that this field must be greater than or equal to the
+specified value, inclusive. If the value of Gte is larger than a specified
+Lt or Lte, the range is reversed.
+
+
var ignore_empty : bool
+
+
IgnoreEmpty specifies that the validation rules of this field should be
+evaluated only if the field is not empty
+
+
var in_ : List[int]
+
+
In specifies that this field must be equal to one of the specified values
+
+
var lt : int
+
+
Lt specifies that this field must be less than the specified value,
+exclusive
+
+
var lte : int
+
+
Lte specifies that this field must be less than or equal to the specified
+value, inclusive
+
+
var not_in : List[int]
+
+
NotIn specifies that this field cannot be equal to one of the specified
+values
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/apis.html b/docs/nitric/resources/apis.html
new file mode 100644
index 0000000..5209d3b
--- /dev/null
+++ b/docs/nitric/resources/apis.html
@@ -0,0 +1,1356 @@
+
+
+
+
+
+
+nitric.resources.apis API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.apis
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+from typing import List, Union
+from dataclasses import dataclass
+from nitric.faas import ApiWorkerOptions, FunctionServer, HttpMiddleware, Middleware, MethodOptions, HttpMethod
+from nitric.application import Nitric
+from nitric.resources.base import BaseResource
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ ApiResource,
+ ApiScopes,
+ ApiSecurityDefinition,
+ ApiSecurityDefinitionJwt,
+ ResourceDeclareRequest,
+ ResourceDetailsRequest,
+)
+from grpclib import GRPCError
+from nitric.api.exception import exception_from_grpc_error
+
+
+@dataclass
+class ApiDetails:
+ """Represents the APIs deployment details."""
+
+ # the identifier of the resource
+ id: str
+ # The provider this resource is deployed with (e.g. aws)
+ provider: str
+ # The service this resource is deployed on (e.g. ApiGateway)
+ service: str
+ # The url of the API
+ url: str
+
+
+@dataclass
+class JwtSecurityDefinition:
+ """Represents the JWT security definition for an API."""
+
+ issuer: str
+ audiences: List[str]
+
+
+# TODO: Union type for multiple security definition mappings
+# type SecurityDefinition = JwtSecurityDefinition;
+
+SecurityDefinition = JwtSecurityDefinition
+
+
+class ApiOptions:
+ """Represents options when creating an API, such as middleware to be applied to all HTTP request to the API."""
+
+ path: str
+ middleware: Union[HttpMiddleware, List[HttpMiddleware], None]
+ security_definitions: Union[dict[str, SecurityDefinition], None]
+ security: Union[dict[str, List[str]], None]
+
+ def __init__(
+ self,
+ path: str = "",
+ middleware: List[Middleware] = [],
+ security_definitions: dict[str, SecurityDefinition] = None,
+ security: dict[str, List[str]] = None,
+ ):
+ """Construct a new API options object."""
+ self.middleware = middleware
+ self.security_definitions = security_definitions
+ self.security = security
+ self.path = path
+
+
+class RouteOptions:
+ """Represents options when creating a route, such as middleware to be applied to all HTTP Methods for the route."""
+
+ middleware: Union[None, List[Middleware]]
+
+ def __init__(self, middleware: List[Middleware] = []):
+ """Construct a new route options object."""
+ self.middleware = middleware
+
+
+def _to_resource(b: Api) -> Resource:
+ return Resource(name=b.name, type=ResourceType.Api)
+
+
+def _security_definition_to_grpc_declaration(
+ security_definitions: dict[str, SecurityDefinition]
+) -> Union[dict[str, ApiSecurityDefinition], None]:
+ if security_definitions is None or len(security_definitions) == 0:
+ return None
+ return {
+ k: ApiSecurityDefinition(jwt=ApiSecurityDefinitionJwt(issuer=v.issuer, audiences=v.audiences))
+ for k, v in security_definitions.items()
+ }
+
+
+def _security_to_grpc_declaration(security: dict[str, List[str]]) -> dict[str, ApiScopes] | None:
+ if security is None or len(security) == 0:
+ return None
+ return {k: ApiScopes(v) for k, v in security.items()}
+
+
+class Api(BaseResource):
+ """An HTTP API."""
+
+ app: Nitric
+ name: str
+ path: str
+ middleware: List[HttpMiddleware]
+ routes: List[Route]
+ security_definitions: dict[str, SecurityDefinition]
+ security: dict[str, List[str]]
+
+ def __init__(self, name: str, opts: ApiOptions = None):
+ """Construct a new HTTP API."""
+ super().__init__()
+ if opts is None:
+ opts = ApiOptions()
+
+ self.name = name
+ self.middleware = opts.middleware if opts.middleware is not None else []
+ self.path = opts.path
+ self.routes = []
+ self.security_definitions = opts.security_definitions
+ self.security = opts.security
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(
+ resource=_to_resource(self),
+ api=ApiResource(
+ security_definitions=_security_definition_to_grpc_declaration(self.security_definitions),
+ security=_security_to_grpc_declaration(self.security),
+ ),
+ )
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _route(self, match: str, opts: RouteOptions = None) -> Route:
+ """Define an HTTP route to be handled by this API."""
+ if opts is None:
+ opts = RouteOptions()
+
+ r = Route(self, match, opts)
+ self.routes.append(r)
+ return r
+
+ def all(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(
+ [
+ HttpMethod.GET,
+ HttpMethod.POST,
+ HttpMethod.PATCH,
+ HttpMethod.PUT,
+ HttpMethod.DELETE,
+ HttpMethod.OPTIONS,
+ ],
+ function,
+ opts=opts,
+ )
+
+ return decorator
+
+ def methods(self, methods: List[HttpMethod], match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(methods, function, opts=opts)
+
+ return decorator
+
+ def get(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.get(function, opts=opts)
+
+ return decorator
+
+ def post(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP POST requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.post(function, opts=opts)
+
+ return decorator
+
+ def delete(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP DELETE requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.delete(function, opts=opts)
+
+ return decorator
+
+ def options(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP OPTIONS requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.options(function, opts=opts)
+
+ return decorator
+
+ def patch(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PATCH requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.patch(function, opts=opts)
+
+ return decorator
+
+ def put(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PUT requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.put(function, opts=opts)
+
+ return decorator
+
+ async def _details(self) -> ApiDetails:
+ """Get the API deployment details."""
+ try:
+ res = await self._resources_stub.details(
+ resource_details_request=ResourceDetailsRequest(
+ resource=_to_resource(self),
+ )
+ )
+ return ApiDetails(res.id, res.provider, res.service, res.api.url)
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ async def URL(self) -> str:
+ """Get the APIs live URL."""
+ details = await self._details()
+ return details.url
+
+
+class Route:
+ """An HTTP route."""
+
+ api: Api
+ path: str
+ middleware: List[Middleware]
+
+ def __init__(self, api: Api, path: str, opts: RouteOptions):
+ """Define a route to be handled by the provided API."""
+ self.api = api
+ self.path = api.path.join(path)
+ self.middleware = opts.middleware if opts.middleware is not None else []
+
+ def method(self, methods: List[HttpMethod], *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for multiple HTTP Methods."""
+ return Method(self, methods, *middleware, opts=opts).start()
+
+ def get(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP GET requests."""
+ return self.method([HttpMethod.GET], *middleware, opts=opts)
+
+ def post(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP POST requests."""
+ return self.method([HttpMethod.POST], *middleware, opts=opts)
+
+ def put(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP PUT requests."""
+ return self.method([HttpMethod.PUT], *middleware, opts=opts)
+
+ def patch(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP PATCH requests."""
+ return self.method([HttpMethod.PATCH], *middleware, opts=opts)
+
+ def delete(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP DELETE requests."""
+ return self.method([HttpMethod.DELETE], *middleware, opts=opts)
+
+ def options(self, *middleware: HttpMiddleware, opts: MethodOptions = None):
+ """Register middleware for HTTP OPTIONS requests."""
+ return self.method([HttpMethod.OPTIONS], *middleware, opts=opts)
+
+
+class Method:
+ """A method handler."""
+
+ server: FunctionServer
+ route: Route
+ methods: List[HttpMethod]
+ opts: MethodOptions
+
+ def __init__(
+ self, route: Route, methods: List[HttpMethod], *middleware: HttpMiddleware, opts: MethodOptions = None
+ ):
+ """Construct a method handler for the specified route."""
+ self.route = route
+ self.methods = methods
+ self.server = FunctionServer(ApiWorkerOptions(route.api.name, route.path, methods, opts))
+ self.server.http(*route.api.middleware, *route.middleware, *middleware)
+
+ def start(self):
+ """Start the server which will respond to incoming requests."""
+ Nitric._register_worker(self.server)
+
+
+def api(name: str, opts: ApiOptions = None) -> Api:
+ """Create a new API resource."""
+ return Nitric._create_resource(Api, name, opts=opts)
+
+
+
+
+
+
+
+
Functions
+
+
+def api(name: str, opts: ApiOptions = None) ‑> Api
+
+
+
Create a new API resource.
+
+
+Expand source code
+
+
def api(name: str, opts: ApiOptions = None) -> Api:
+ """Create a new API resource."""
+ return Nitric._create_resource(Api, name, opts=opts)
+
+
+
+
+
+
Classes
+
+
+class Api
+(name: str, opts: ApiOptions = None)
+
+
+
An HTTP API.
+
Construct a new HTTP API.
+
+
+Expand source code
+
+
class Api(BaseResource):
+ """An HTTP API."""
+
+ app: Nitric
+ name: str
+ path: str
+ middleware: List[HttpMiddleware]
+ routes: List[Route]
+ security_definitions: dict[str, SecurityDefinition]
+ security: dict[str, List[str]]
+
+ def __init__(self, name: str, opts: ApiOptions = None):
+ """Construct a new HTTP API."""
+ super().__init__()
+ if opts is None:
+ opts = ApiOptions()
+
+ self.name = name
+ self.middleware = opts.middleware if opts.middleware is not None else []
+ self.path = opts.path
+ self.routes = []
+ self.security_definitions = opts.security_definitions
+ self.security = opts.security
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(
+ resource=_to_resource(self),
+ api=ApiResource(
+ security_definitions=_security_definition_to_grpc_declaration(self.security_definitions),
+ security=_security_to_grpc_declaration(self.security),
+ ),
+ )
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _route(self, match: str, opts: RouteOptions = None) -> Route:
+ """Define an HTTP route to be handled by this API."""
+ if opts is None:
+ opts = RouteOptions()
+
+ r = Route(self, match, opts)
+ self.routes.append(r)
+ return r
+
+ def all(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(
+ [
+ HttpMethod.GET,
+ HttpMethod.POST,
+ HttpMethod.PATCH,
+ HttpMethod.PUT,
+ HttpMethod.DELETE,
+ HttpMethod.OPTIONS,
+ ],
+ function,
+ opts=opts,
+ )
+
+ return decorator
+
+ def methods(self, methods: List[HttpMethod], match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(methods, function, opts=opts)
+
+ return decorator
+
+ def get(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.get(function, opts=opts)
+
+ return decorator
+
+ def post(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP POST requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.post(function, opts=opts)
+
+ return decorator
+
+ def delete(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP DELETE requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.delete(function, opts=opts)
+
+ return decorator
+
+ def options(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP OPTIONS requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.options(function, opts=opts)
+
+ return decorator
+
+ def patch(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PATCH requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.patch(function, opts=opts)
+
+ return decorator
+
+ def put(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PUT requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.put(function, opts=opts)
+
+ return decorator
+
+ async def _details(self) -> ApiDetails:
+ """Get the API deployment details."""
+ try:
+ res = await self._resources_stub.details(
+ resource_details_request=ResourceDetailsRequest(
+ resource=_to_resource(self),
+ )
+ )
+ return ApiDetails(res.id, res.provider, res.service, res.api.url)
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ async def URL(self) -> str:
+ """Get the APIs live URL."""
+ details = await self._details()
+ return details.url
@dataclass
+class ApiDetails:
+ """Represents the APIs deployment details."""
+
+ # the identifier of the resource
+ id: str
+ # The provider this resource is deployed with (e.g. aws)
+ provider: str
+ # The service this resource is deployed on (e.g. ApiGateway)
+ service: str
+ # The url of the API
+ url: str
Represents options when creating a route, such as middleware to be applied to all HTTP Methods for the route.
+
Construct a new route options object.
+
+
+Expand source code
+
+
class RouteOptions:
+ """Represents options when creating a route, such as middleware to be applied to all HTTP Methods for the route."""
+
+ middleware: Union[None, List[Middleware]]
+
+ def __init__(self, middleware: List[Middleware] = []):
+ """Construct a new route options object."""
+ self.middleware = middleware
+
+
Class variables
+
+
var middleware : Optional[List[Callable[[~C, Callable], Coroutine[Any, Any, ~C]]]]
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/base.html b/docs/nitric/resources/base.html
new file mode 100644
index 0000000..a82c334
--- /dev/null
+++ b/docs/nitric/resources/base.html
@@ -0,0 +1,354 @@
+
+
+
+
+
+
+nitric.resources.base API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.base
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+import asyncio
+from abc import ABC, abstractmethod
+from asyncio import Task
+
+from typing import TypeVar, Type, Union, List
+
+from grpclib import GRPCError
+from nitric.proto.nitric.resource.v1 import (
+ Action,
+ PolicyResource,
+ Resource,
+ ResourceType,
+ ResourceDeclareRequest,
+ ResourceServiceStub,
+)
+
+from nitric.api.exception import exception_from_grpc_error, NitricResourceException
+from nitric.utils import new_default_channel
+
+T = TypeVar("T", bound="BaseResource")
+
+
+class BaseResource(ABC):
+ """A base resource class with common functionality."""
+
+ cache = {}
+
+ def __init__(self):
+ """Construct a new resource."""
+ self._reg: Union[Task, None] = None
+ self._channel = new_default_channel()
+ self._resources_stub = ResourceServiceStub(channel=self._channel)
+
+ @abstractmethod
+ async def _register(self):
+ pass
+
+ @classmethod
+ def make(cls: Type[T], name: str, *args, **kwargs) -> T:
+ """
+ Create and register the resource.
+
+ The registration process for resources async, so this method should be used instead of __init__.
+ """
+ # Todo: store the resource reference in a cache to avoid duplicate registrations
+ r = cls(name, *args, **kwargs)
+ try:
+ loop = asyncio.get_running_loop()
+ r._reg = loop.create_task(r._register())
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(r._register())
+
+ return r
+
+
+class SecureResource(BaseResource):
+ """A secure base resource class."""
+
+ @abstractmethod
+ def _to_resource(self) -> Resource:
+ pass
+
+ @abstractmethod
+ def _perms_to_actions(self, *args: str) -> List[Action]:
+ pass
+
+ async def _register_policy_async(self, *args: str):
+ # if self._reg is not None:
+ # await asyncio.wait({self._reg})
+
+ policy = PolicyResource(
+ principals=[Resource(type=ResourceType.Function)],
+ actions=self._perms_to_actions(*args),
+ resources=[self._to_resource()],
+ )
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(
+ resource=Resource(type=ResourceType.Policy), policy=policy
+ )
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _register_policy(self, *args: str):
+ try:
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(self._register_policy_async(*args))
+ except RuntimeError:
+ # TODO: Check nitric runtime ENV variable
+ raise NitricResourceException(
+ "Nitric resources cannot be declared at runtime e.g. within the scope of a function. \
+ Move resource declarations to the top level of scripts so that they can be safely provisioned"
+ )
+
+
+
+
+
+
+
+
+
+
Classes
+
+
+class BaseResource
+
+
+
A base resource class with common functionality.
+
Construct a new resource.
+
+
+Expand source code
+
+
class BaseResource(ABC):
+ """A base resource class with common functionality."""
+
+ cache = {}
+
+ def __init__(self):
+ """Construct a new resource."""
+ self._reg: Union[Task, None] = None
+ self._channel = new_default_channel()
+ self._resources_stub = ResourceServiceStub(channel=self._channel)
+
+ @abstractmethod
+ async def _register(self):
+ pass
+
+ @classmethod
+ def make(cls: Type[T], name: str, *args, **kwargs) -> T:
+ """
+ Create and register the resource.
+
+ The registration process for resources async, so this method should be used instead of __init__.
+ """
+ # Todo: store the resource reference in a cache to avoid duplicate registrations
+ r = cls(name, *args, **kwargs)
+ try:
+ loop = asyncio.get_running_loop()
+ r._reg = loop.create_task(r._register())
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(r._register())
+
+ return r
The registration process for resources async, so this method should be used instead of init.
+
+
+Expand source code
+
+
@classmethod
+def make(cls: Type[T], name: str, *args, **kwargs) -> T:
+ """
+ Create and register the resource.
+
+ The registration process for resources async, so this method should be used instead of __init__.
+ """
+ # Todo: store the resource reference in a cache to avoid duplicate registrations
+ r = cls(name, *args, **kwargs)
+ try:
+ loop = asyncio.get_running_loop()
+ r._reg = loop.create_task(r._register())
+ except RuntimeError:
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(r._register())
+
+ return r
+
+
+
+
+
+class SecureResource
+
+
+
A secure base resource class.
+
Construct a new resource.
+
+
+Expand source code
+
+
class SecureResource(BaseResource):
+ """A secure base resource class."""
+
+ @abstractmethod
+ def _to_resource(self) -> Resource:
+ pass
+
+ @abstractmethod
+ def _perms_to_actions(self, *args: str) -> List[Action]:
+ pass
+
+ async def _register_policy_async(self, *args: str):
+ # if self._reg is not None:
+ # await asyncio.wait({self._reg})
+
+ policy = PolicyResource(
+ principals=[Resource(type=ResourceType.Function)],
+ actions=self._perms_to_actions(*args),
+ resources=[self._to_resource()],
+ )
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(
+ resource=Resource(type=ResourceType.Policy), policy=policy
+ )
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _register_policy(self, *args: str):
+ try:
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(self._register_policy_async(*args))
+ except RuntimeError:
+ # TODO: Check nitric runtime ENV variable
+ raise NitricResourceException(
+ "Nitric resources cannot be declared at runtime e.g. within the scope of a function. \
+ Move resource declarations to the top level of scripts so that they can be safely provisioned"
+ )
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/buckets.html b/docs/nitric/resources/buckets.html
new file mode 100644
index 0000000..9c7eb41
--- /dev/null
+++ b/docs/nitric/resources/buckets.html
@@ -0,0 +1,336 @@
+
+
+
+
+
+
+nitric.resources.buckets API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.buckets
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+from nitric.api.exception import exception_from_grpc_error
+from nitric.api.storage import BucketRef, Storage
+from typing import List, Union
+from enum import Enum
+from grpclib import GRPCError
+
+from nitric.application import Nitric
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ Action,
+ ResourceDeclareRequest,
+)
+
+from nitric.resources.base import SecureResource
+
+
+class BucketPermission(Enum):
+ """Valid query expression operators."""
+
+ reading = "reading"
+ writing = "writing"
+ deleting = "deleting"
+
+
+class Bucket(SecureResource):
+ """A bucket resource, used for storage and retrieval of blob/binary data."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Create a bucket with the name provided or references it if it already exists."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _perms_to_actions(self, *args: [Union[BucketPermission, str]]) -> List[Action]:
+ permission_actions_map = {
+ BucketPermission.reading: [Action.BucketFileGet, Action.BucketFileList],
+ BucketPermission.writing: [Action.BucketFilePut],
+ BucketPermission.deleting: [Action.BucketFileDelete],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, BucketPermission) else BucketPermission[permission.lower()]
+ for permission in args
+ ]
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Bucket)
+
+ def allow(self, *args: Union[BucketPermission, str]) -> BucketRef:
+ """Request the required permissions for this resource."""
+ self._register_policy(*args)
+
+ return Storage().bucket(self.name)
+
+
+def bucket(name: str) -> Bucket:
+ """
+ Create and register a bucket.
+
+ If a bucket has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Bucket, name)
If a bucket has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def bucket(name: str) -> Bucket:
+ """
+ Create and register a bucket.
+
+ If a bucket has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Bucket, name)
+
+
+
+
+
+
Classes
+
+
+class Bucket
+(name: str)
+
+
+
A bucket resource, used for storage and retrieval of blob/binary data.
+
Create a bucket with the name provided or references it if it already exists.
+
+
+Expand source code
+
+
class Bucket(SecureResource):
+ """A bucket resource, used for storage and retrieval of blob/binary data."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Create a bucket with the name provided or references it if it already exists."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _perms_to_actions(self, *args: [Union[BucketPermission, str]]) -> List[Action]:
+ permission_actions_map = {
+ BucketPermission.reading: [Action.BucketFileGet, Action.BucketFileList],
+ BucketPermission.writing: [Action.BucketFilePut],
+ BucketPermission.deleting: [Action.BucketFileDelete],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, BucketPermission) else BucketPermission[permission.lower()]
+ for permission in args
+ ]
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Bucket)
+
+ def allow(self, *args: Union[BucketPermission, str]) -> BucketRef:
+ """Request the required permissions for this resource."""
+ self._register_policy(*args)
+
+ return Storage().bucket(self.name)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/collections.html b/docs/nitric/resources/collections.html
new file mode 100644
index 0000000..1ae62ae
--- /dev/null
+++ b/docs/nitric/resources/collections.html
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+nitric.resources.collections API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.collections
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+from nitric.api.documents import CollectionRef, Documents
+from nitric.api.exception import exception_from_grpc_error
+from typing import List, Union
+from enum import Enum
+from grpclib import GRPCError
+
+from nitric.application import Nitric
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ Action,
+ ResourceDeclareRequest,
+)
+
+from nitric.resources.base import SecureResource
+
+
+class CollectionPermission(Enum):
+ """Valid query expression operators."""
+
+ reading = "reading"
+ writing = "writing"
+ deleting = "deleting"
+
+
+class Collection(SecureResource):
+ """A document collection resource."""
+
+ def __init__(self, name: str):
+ """Construct a new document collection."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Collection)
+
+ def _perms_to_actions(self, *args: Union[CollectionPermission, str]) -> List[Action]:
+ permission_actions_map = {
+ CollectionPermission.reading: [
+ Action.CollectionDocumentRead,
+ Action.CollectionQuery,
+ Action.CollectionList,
+ ],
+ CollectionPermission.writing: [Action.CollectionDocumentWrite, Action.CollectionList],
+ CollectionPermission.deleting: [Action.CollectionDocumentDelete, Action.CollectionList],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, CollectionPermission) else CollectionPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ def allow(self, *args: Union[CollectionPermission, str]) -> CollectionRef:
+ """Request the required permissions for this collection."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Documents().collection(self.name)
+
+
+def collection(name: str) -> Collection:
+ """
+ Create and register a collection.
+
+ If a collection has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Collection, name)
If a collection has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def collection(name: str) -> Collection:
+ """
+ Create and register a collection.
+
+ If a collection has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Collection, name)
+
+
+
+
+
+
Classes
+
+
+class Collection
+(name: str)
+
+
+
A document collection resource.
+
Construct a new document collection.
+
+
+Expand source code
+
+
class Collection(SecureResource):
+ """A document collection resource."""
+
+ def __init__(self, name: str):
+ """Construct a new document collection."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Collection)
+
+ def _perms_to_actions(self, *args: Union[CollectionPermission, str]) -> List[Action]:
+ permission_actions_map = {
+ CollectionPermission.reading: [
+ Action.CollectionDocumentRead,
+ Action.CollectionQuery,
+ Action.CollectionList,
+ ],
+ CollectionPermission.writing: [Action.CollectionDocumentWrite, Action.CollectionList],
+ CollectionPermission.deleting: [Action.CollectionDocumentDelete, Action.CollectionList],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, CollectionPermission) else CollectionPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ def allow(self, *args: Union[CollectionPermission, str]) -> CollectionRef:
+ """Request the required permissions for this collection."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Documents().collection(self.name)
Request the required permissions for this collection.
+
+
+Expand source code
+
+
def allow(self, *args: Union[CollectionPermission, str]) -> CollectionRef:
+ """Request the required permissions for this collection."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Documents().collection(self.name)
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Nitric Python SDK API Documentation. See: https://nitric.io/docs?lang=python for full framework documentation."""
+
+from nitric.resources.apis import Api, api, MethodOptions, ApiOptions, ApiDetails, JwtSecurityDefinition
+from nitric.resources.buckets import Bucket, bucket
+from nitric.resources.collections import Collection, collection
+from nitric.resources.queues import Queue, queue
+from nitric.resources.schedules import Schedule, schedule
+from nitric.resources.secrets import Secret, secret
+from nitric.resources.topics import Topic, topic
+
+__all__ = [
+ "api",
+ "Api",
+ "ApiOptions",
+ "ApiDetails",
+ "JwtSecurityDefinition",
+ "MethodOptions",
+ "bucket",
+ "Bucket",
+ "collection",
+ "Collection",
+ "queue",
+ "Queue",
+ "Schedule",
+ "schedule",
+ "secret",
+ "Secret",
+ "topic",
+ "Topic",
+]
If a bucket has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def bucket(name: str) -> Bucket:
+ """
+ Create and register a bucket.
+
+ If a bucket has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Bucket, name)
If a collection has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def collection(name: str) -> Collection:
+ """
+ Create and register a collection.
+
+ If a collection has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Collection, name)
If a queue has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def queue(name: str) -> Queue:
+ """
+ Create and register a queue.
+
+ If a queue has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Queue, name)
+
+
+
+def schedule(description: str, every: str)
+
+
+
Return a schedule decorator.
+
+
+Expand source code
+
+
def schedule(description: str, every: str):
+ """Return a schedule decorator."""
+
+ def decorator(func: EventMiddleware):
+ r = Schedule(description)
+ r.every(every, func)
+ return r
+
+ return decorator
If a secret has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def secret(name: str) -> Secret:
+ """
+ Create and registers a secret.
+
+ If a secret has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Secret, name)
If a topic has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def topic(name: str) -> Topic:
+ """
+ Create and register a topic.
+
+ If a topic has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Topic, name)
+
+
+
+
+
+
Classes
+
+
+class Api
+(name: str, opts: ApiOptions = None)
+
+
+
An HTTP API.
+
Construct a new HTTP API.
+
+
+Expand source code
+
+
class Api(BaseResource):
+ """An HTTP API."""
+
+ app: Nitric
+ name: str
+ path: str
+ middleware: List[HttpMiddleware]
+ routes: List[Route]
+ security_definitions: dict[str, SecurityDefinition]
+ security: dict[str, List[str]]
+
+ def __init__(self, name: str, opts: ApiOptions = None):
+ """Construct a new HTTP API."""
+ super().__init__()
+ if opts is None:
+ opts = ApiOptions()
+
+ self.name = name
+ self.middleware = opts.middleware if opts.middleware is not None else []
+ self.path = opts.path
+ self.routes = []
+ self.security_definitions = opts.security_definitions
+ self.security = opts.security
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(
+ resource=_to_resource(self),
+ api=ApiResource(
+ security_definitions=_security_definition_to_grpc_declaration(self.security_definitions),
+ security=_security_to_grpc_declaration(self.security),
+ ),
+ )
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _route(self, match: str, opts: RouteOptions = None) -> Route:
+ """Define an HTTP route to be handled by this API."""
+ if opts is None:
+ opts = RouteOptions()
+
+ r = Route(self, match, opts)
+ self.routes.append(r)
+ return r
+
+ def all(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(
+ [
+ HttpMethod.GET,
+ HttpMethod.POST,
+ HttpMethod.PATCH,
+ HttpMethod.PUT,
+ HttpMethod.DELETE,
+ HttpMethod.OPTIONS,
+ ],
+ function,
+ opts=opts,
+ )
+
+ return decorator
+
+ def methods(self, methods: List[HttpMethod], match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.method(methods, function, opts=opts)
+
+ return decorator
+
+ def get(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP GET requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.get(function, opts=opts)
+
+ return decorator
+
+ def post(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP POST requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.post(function, opts=opts)
+
+ return decorator
+
+ def delete(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP DELETE requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.delete(function, opts=opts)
+
+ return decorator
+
+ def options(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP OPTIONS requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.options(function, opts=opts)
+
+ return decorator
+
+ def patch(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PATCH requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.patch(function, opts=opts)
+
+ return decorator
+
+ def put(self, match: str, opts: MethodOptions = None):
+ """Define an HTTP route which will respond to HTTP PUT requests."""
+ if opts is None:
+ opts = MethodOptions()
+
+ def decorator(function: HttpMiddleware):
+ r = self._route(match)
+ r.put(function, opts=opts)
+
+ return decorator
+
+ async def _details(self) -> ApiDetails:
+ """Get the API deployment details."""
+ try:
+ res = await self._resources_stub.details(
+ resource_details_request=ResourceDetailsRequest(
+ resource=_to_resource(self),
+ )
+ )
+ return ApiDetails(res.id, res.provider, res.service, res.api.url)
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ async def URL(self) -> str:
+ """Get the APIs live URL."""
+ details = await self._details()
+ return details.url
@dataclass
+class ApiDetails:
+ """Represents the APIs deployment details."""
+
+ # the identifier of the resource
+ id: str
+ # The provider this resource is deployed with (e.g. aws)
+ provider: str
+ # The service this resource is deployed on (e.g. ApiGateway)
+ service: str
+ # The url of the API
+ url: str
Request the required permissions for this collection.
+
+
+Expand source code
+
+
def allow(self, *args: Union[CollectionPermission, str]) -> CollectionRef:
+ """Request the required permissions for this collection."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Documents().collection(self.name)
def allow(self, *args: Union[QueuePermission, str]) -> QueueRef:
+ """Request the required permissions for this queue."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Queues().queue(self.name)
class Schedule:
+ """A schedule for running functions on a cadence."""
+
+ description: str
+ server: FunctionServer
+
+ def start(self):
+ """Start the function server that executes the scheduled middleware."""
+ return self.server.start()
+
+ def __init__(self, description: str):
+ """Construct a new schedule."""
+ self.description = description
+
+ def every(self, rate_description: str, *middleware: EventMiddleware):
+ """
+ Register middleware to be run at the specified rate.
+
+ E.g. every("3 hours")
+ """
+ rate_description = rate_description.lower()
+
+ if not any([frequency in rate_description for frequency in Frequency.as_str_list()]):
+ # handle singular frequencies. e.g. every('day')
+ rate_description = f"1 {rate_description}s" # 'day' becomes '1 days'
+
+ rate, freq_str = rate_description.split(" ")
+ freq = Frequency.from_str(freq_str)
+
+ if not rate.isdigit():
+ raise Exception("invalid rate expression, expression must begin with a positive integer")
+
+ if not freq:
+ raise Exception(
+ f"invalid rate expression, frequency must be one of ${Frequency.as_str_list()}, received ${freq_str}"
+ )
+
+ opts = RateWorkerOptions(self.description, int(rate), freq)
+
+ self.server = FunctionServer(opts)
+ self.server.event(*middleware)
+ return Nitric._register_worker(self.server)
Register middleware to be run at the specified rate.
+
E.g. every("3 hours")
+
+
+Expand source code
+
+
def every(self, rate_description: str, *middleware: EventMiddleware):
+ """
+ Register middleware to be run at the specified rate.
+
+ E.g. every("3 hours")
+ """
+ rate_description = rate_description.lower()
+
+ if not any([frequency in rate_description for frequency in Frequency.as_str_list()]):
+ # handle singular frequencies. e.g. every('day')
+ rate_description = f"1 {rate_description}s" # 'day' becomes '1 days'
+
+ rate, freq_str = rate_description.split(" ")
+ freq = Frequency.from_str(freq_str)
+
+ if not rate.isdigit():
+ raise Exception("invalid rate expression, expression must begin with a positive integer")
+
+ if not freq:
+ raise Exception(
+ f"invalid rate expression, frequency must be one of ${Frequency.as_str_list()}, received ${freq_str}"
+ )
+
+ opts = RateWorkerOptions(self.description, int(rate), freq)
+
+ self.server = FunctionServer(opts)
+ self.server.event(*middleware)
+ return Nitric._register_worker(self.server)
+
+
+
+def start(self)
+
+
+
Start the function server that executes the scheduled middleware.
+
+
+Expand source code
+
+
def start(self):
+ """Start the function server that executes the scheduled middleware."""
+ return self.server.start()
+
+
+
+
+
+class Secret
+(name: str)
+
+
+
A secret resource, used for storing and retrieving secret versions and values.
+
Construct a new secret resource reference.
+
+
+Expand source code
+
+
class Secret(SecureResource):
+ """A secret resource, used for storing and retrieving secret versions and values."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Construct a new secret resource reference."""
+ super().__init__()
+ self.name = name
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Secret)
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _perms_to_actions(self, *args: Union[SecretPermission, str]) -> List[Action]:
+ permissions_actions_map = {
+ SecretPermission.accessing: [Action.SecretAccess],
+ SecretPermission.putting: [Action.SecretPut],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, SecretPermission) else SecretPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permissions_actions_map[perm]]
+
+ def allow(self, *args: Union[SecretPermission, str]) -> SecretContainerRef:
+ """Request the specified permissions to this resource."""
+ self._register_policy(*args)
+
+ return Secrets().secret(self.name)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/queues.html b/docs/nitric/resources/queues.html
new file mode 100644
index 0000000..46d7c00
--- /dev/null
+++ b/docs/nitric/resources/queues.html
@@ -0,0 +1,331 @@
+
+
+
+
+
+
+nitric.resources.queues API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.queues
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+from nitric.api.exception import exception_from_grpc_error
+from typing import List, Union
+from enum import Enum
+from grpclib import GRPCError
+from nitric.api.queues import QueueRef, Queues
+from nitric.application import Nitric
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ Action,
+ ResourceDeclareRequest,
+)
+
+from nitric.resources.base import SecureResource
+
+
+class QueuePermission(Enum):
+ """Valid query expression operators."""
+
+ sending = "sending"
+ receiving = "receiving"
+
+
+class Queue(SecureResource):
+ """A queue resource."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Construct a new queue resource."""
+ super().__init__()
+ self.name = name
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Queue)
+
+ def _perms_to_actions(self, *args: Union[QueuePermission, str]) -> List[Action]:
+ permission_actions_map = {
+ QueuePermission.sending: [Action.QueueSend, Action.QueueList, Action.QueueDetail],
+ QueuePermission.receiving: [Action.QueueReceive, Action.QueueList, Action.QueueDetail],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, QueuePermission) else QueuePermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def allow(self, *args: Union[QueuePermission, str]) -> QueueRef:
+ """Request the required permissions for this queue."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Queues().queue(self.name)
+
+
+def queue(name: str) -> Queue:
+ """
+ Create and register a queue.
+
+ If a queue has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Queue, name)
If a queue has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def queue(name: str) -> Queue:
+ """
+ Create and register a queue.
+
+ If a queue has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Queue, name)
+
+
+
+
+
+
Classes
+
+
+class Queue
+(name: str)
+
+
+
A queue resource.
+
Construct a new queue resource.
+
+
+Expand source code
+
+
class Queue(SecureResource):
+ """A queue resource."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Construct a new queue resource."""
+ super().__init__()
+ self.name = name
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Queue)
+
+ def _perms_to_actions(self, *args: Union[QueuePermission, str]) -> List[Action]:
+ permission_actions_map = {
+ QueuePermission.sending: [Action.QueueSend, Action.QueueList, Action.QueueDetail],
+ QueuePermission.receiving: [Action.QueueReceive, Action.QueueList, Action.QueueDetail],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, QueuePermission) else QueuePermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permission_actions_map[perm]]
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def allow(self, *args: Union[QueuePermission, str]) -> QueueRef:
+ """Request the required permissions for this queue."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Queues().queue(self.name)
def allow(self, *args: Union[QueuePermission, str]) -> QueueRef:
+ """Request the required permissions for this queue."""
+ # Ensure registration of the resource is complete before requesting permissions.
+ self._register_policy(*args)
+
+ return Queues().queue(self.name)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/schedules.html b/docs/nitric/resources/schedules.html
new file mode 100644
index 0000000..6337e09
--- /dev/null
+++ b/docs/nitric/resources/schedules.html
@@ -0,0 +1,301 @@
+
+
+
+
+
+
+nitric.resources.schedules API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.schedules
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+from nitric.application import Nitric
+from nitric.faas import FunctionServer, EventMiddleware, RateWorkerOptions, Frequency
+
+
+class Schedule:
+ """A schedule for running functions on a cadence."""
+
+ description: str
+ server: FunctionServer
+
+ def start(self):
+ """Start the function server that executes the scheduled middleware."""
+ return self.server.start()
+
+ def __init__(self, description: str):
+ """Construct a new schedule."""
+ self.description = description
+
+ def every(self, rate_description: str, *middleware: EventMiddleware):
+ """
+ Register middleware to be run at the specified rate.
+
+ E.g. every("3 hours")
+ """
+ rate_description = rate_description.lower()
+
+ if not any([frequency in rate_description for frequency in Frequency.as_str_list()]):
+ # handle singular frequencies. e.g. every('day')
+ rate_description = f"1 {rate_description}s" # 'day' becomes '1 days'
+
+ rate, freq_str = rate_description.split(" ")
+ freq = Frequency.from_str(freq_str)
+
+ if not rate.isdigit():
+ raise Exception("invalid rate expression, expression must begin with a positive integer")
+
+ if not freq:
+ raise Exception(
+ f"invalid rate expression, frequency must be one of ${Frequency.as_str_list()}, received ${freq_str}"
+ )
+
+ opts = RateWorkerOptions(self.description, int(rate), freq)
+
+ self.server = FunctionServer(opts)
+ self.server.event(*middleware)
+ return Nitric._register_worker(self.server)
+
+
+def schedule(description: str, every: str):
+ """Return a schedule decorator."""
+
+ def decorator(func: EventMiddleware):
+ r = Schedule(description)
+ r.every(every, func)
+ return r
+
+ return decorator
+
+
+
+
+
+
+
+
Functions
+
+
+def schedule(description: str, every: str)
+
+
+
Return a schedule decorator.
+
+
+Expand source code
+
+
def schedule(description: str, every: str):
+ """Return a schedule decorator."""
+
+ def decorator(func: EventMiddleware):
+ r = Schedule(description)
+ r.every(every, func)
+ return r
+
+ return decorator
+
+
+
+
+
+
Classes
+
+
+class Schedule
+(description: str)
+
+
+
A schedule for running functions on a cadence.
+
Construct a new schedule.
+
+
+Expand source code
+
+
class Schedule:
+ """A schedule for running functions on a cadence."""
+
+ description: str
+ server: FunctionServer
+
+ def start(self):
+ """Start the function server that executes the scheduled middleware."""
+ return self.server.start()
+
+ def __init__(self, description: str):
+ """Construct a new schedule."""
+ self.description = description
+
+ def every(self, rate_description: str, *middleware: EventMiddleware):
+ """
+ Register middleware to be run at the specified rate.
+
+ E.g. every("3 hours")
+ """
+ rate_description = rate_description.lower()
+
+ if not any([frequency in rate_description for frequency in Frequency.as_str_list()]):
+ # handle singular frequencies. e.g. every('day')
+ rate_description = f"1 {rate_description}s" # 'day' becomes '1 days'
+
+ rate, freq_str = rate_description.split(" ")
+ freq = Frequency.from_str(freq_str)
+
+ if not rate.isdigit():
+ raise Exception("invalid rate expression, expression must begin with a positive integer")
+
+ if not freq:
+ raise Exception(
+ f"invalid rate expression, frequency must be one of ${Frequency.as_str_list()}, received ${freq_str}"
+ )
+
+ opts = RateWorkerOptions(self.description, int(rate), freq)
+
+ self.server = FunctionServer(opts)
+ self.server.event(*middleware)
+ return Nitric._register_worker(self.server)
Register middleware to be run at the specified rate.
+
E.g. every("3 hours")
+
+
+Expand source code
+
+
def every(self, rate_description: str, *middleware: EventMiddleware):
+ """
+ Register middleware to be run at the specified rate.
+
+ E.g. every("3 hours")
+ """
+ rate_description = rate_description.lower()
+
+ if not any([frequency in rate_description for frequency in Frequency.as_str_list()]):
+ # handle singular frequencies. e.g. every('day')
+ rate_description = f"1 {rate_description}s" # 'day' becomes '1 days'
+
+ rate, freq_str = rate_description.split(" ")
+ freq = Frequency.from_str(freq_str)
+
+ if not rate.isdigit():
+ raise Exception("invalid rate expression, expression must begin with a positive integer")
+
+ if not freq:
+ raise Exception(
+ f"invalid rate expression, frequency must be one of ${Frequency.as_str_list()}, received ${freq_str}"
+ )
+
+ opts = RateWorkerOptions(self.description, int(rate), freq)
+
+ self.server = FunctionServer(opts)
+ self.server.event(*middleware)
+ return Nitric._register_worker(self.server)
+
+
+
+def start(self)
+
+
+
Start the function server that executes the scheduled middleware.
+
+
+Expand source code
+
+
def start(self):
+ """Start the function server that executes the scheduled middleware."""
+ return self.server.start()
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/secrets.html b/docs/nitric/resources/secrets.html
new file mode 100644
index 0000000..998fb5a
--- /dev/null
+++ b/docs/nitric/resources/secrets.html
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+nitric.resources.secrets API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.secrets
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+from nitric.api.exception import exception_from_grpc_error
+from typing import List, Union
+from enum import Enum
+from grpclib import GRPCError
+
+from nitric.application import Nitric
+from nitric.api.secrets import Secrets, SecretContainerRef
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ Action,
+ ResourceDeclareRequest,
+)
+
+from nitric.resources.base import SecureResource
+
+
+class SecretPermission(Enum):
+ """Available permissions that can be requested for secret resources."""
+
+ accessing = "accessing"
+ putting = "putting"
+
+
+class Secret(SecureResource):
+ """A secret resource, used for storing and retrieving secret versions and values."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Construct a new secret resource reference."""
+ super().__init__()
+ self.name = name
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Secret)
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _perms_to_actions(self, *args: Union[SecretPermission, str]) -> List[Action]:
+ permissions_actions_map = {
+ SecretPermission.accessing: [Action.SecretAccess],
+ SecretPermission.putting: [Action.SecretPut],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, SecretPermission) else SecretPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permissions_actions_map[perm]]
+
+ def allow(self, *args: Union[SecretPermission, str]) -> SecretContainerRef:
+ """Request the specified permissions to this resource."""
+ self._register_policy(*args)
+
+ return Secrets().secret(self.name)
+
+
+#
+def secret(name: str) -> Secret:
+ """
+ Create and registers a secret.
+
+ If a secret has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Secret, name)
If a secret has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def secret(name: str) -> Secret:
+ """
+ Create and registers a secret.
+
+ If a secret has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Secret, name)
+
+
+
+
+
+
Classes
+
+
+class Secret
+(name: str)
+
+
+
A secret resource, used for storing and retrieving secret versions and values.
+
Construct a new secret resource reference.
+
+
+Expand source code
+
+
class Secret(SecureResource):
+ """A secret resource, used for storing and retrieving secret versions and values."""
+
+ name: str
+ actions: List[Action]
+
+ def __init__(self, name: str):
+ """Construct a new secret resource reference."""
+ super().__init__()
+ self.name = name
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Secret)
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _perms_to_actions(self, *args: Union[SecretPermission, str]) -> List[Action]:
+ permissions_actions_map = {
+ SecretPermission.accessing: [Action.SecretAccess],
+ SecretPermission.putting: [Action.SecretPut],
+ }
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, SecretPermission) else SecretPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in permissions_actions_map[perm]]
+
+ def allow(self, *args: Union[SecretPermission, str]) -> SecretContainerRef:
+ """Request the specified permissions to this resource."""
+ self._register_policy(*args)
+
+ return Secrets().secret(self.name)
Available permissions that can be requested for secret resources.
+
+
+Expand source code
+
+
class SecretPermission(Enum):
+ """Available permissions that can be requested for secret resources."""
+
+ accessing = "accessing"
+ putting = "putting"
+
+
Ancestors
+
+
enum.Enum
+
+
Class variables
+
+
var accessing
+
+
+
+
var putting
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/nitric/resources/topics.html b/docs/nitric/resources/topics.html
new file mode 100644
index 0000000..05fb81a
--- /dev/null
+++ b/docs/nitric/resources/topics.html
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+nitric.resources.topics API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Module nitric.resources.topics
+
+
+
+
+Expand source code
+
+
#
+# Copyright (c) 2021 Nitric Technologies Pty Ltd.
+#
+# This file is part of Nitric Python 3 SDK.
+# See https://github.com/nitrictech/python-sdk for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import annotations
+
+from nitric.api.events import Events, TopicRef
+from nitric.api.exception import exception_from_grpc_error
+from typing import List, Union
+from enum import Enum
+from grpclib import GRPCError
+from nitric.application import Nitric
+from nitric.faas import EventMiddleware, FunctionServer, SubscriptionWorkerOptions
+from nitric.proto.nitric.resource.v1 import (
+ Resource,
+ ResourceType,
+ Action,
+ ResourceDeclareRequest,
+)
+
+from nitric.resources.base import SecureResource
+
+
+class TopicPermission(Enum):
+ """Valid query expression operators."""
+
+ publishing = "publishing"
+
+
+class Topic(SecureResource):
+ """A topic resource, used for asynchronous messaging between functions."""
+
+ name: str
+ actions: List[Action]
+ server: FunctionServer
+
+ def __init__(self, name: str):
+ """Construct a new topic."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Topic)
+
+ def _perms_to_actions(self, *args: Union[TopicPermission, str]) -> List[Action]:
+ _permMap = {TopicPermission.publishing: [Action.TopicEventPublish]}
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, TopicPermission) else TopicPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in _permMap[perm]]
+
+ def allow(self, *args: Union[TopicPermission, str]) -> TopicRef:
+ """Request the specified permissions to this resource."""
+ self._register_policy(*args)
+
+ return Events().topic(self.name)
+
+ def subscribe(self, func: EventMiddleware):
+ """Create and return a subscription decorator for this topic."""
+ self.server = FunctionServer(SubscriptionWorkerOptions(topic=self.name))
+ self.server.event(func)
+ Nitric._register_worker(self.server)
+
+
+def topic(name: str) -> Topic:
+ """
+ Create and register a topic.
+
+ If a topic has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Topic, name)
If a topic has already been registered with the same name, the original reference will be reused.
+
+
+Expand source code
+
+
def topic(name: str) -> Topic:
+ """
+ Create and register a topic.
+
+ If a topic has already been registered with the same name, the original reference will be reused.
+ """
+ return Nitric._create_resource(Topic, name)
+
+
+
+
+
+
Classes
+
+
+class Topic
+(name: str)
+
+
+
A topic resource, used for asynchronous messaging between functions.
+
Construct a new topic.
+
+
+Expand source code
+
+
class Topic(SecureResource):
+ """A topic resource, used for asynchronous messaging between functions."""
+
+ name: str
+ actions: List[Action]
+ server: FunctionServer
+
+ def __init__(self, name: str):
+ """Construct a new topic."""
+ super().__init__()
+ self.name = name
+
+ async def _register(self):
+ try:
+ await self._resources_stub.declare(
+ resource_declare_request=ResourceDeclareRequest(resource=self._to_resource())
+ )
+ except GRPCError as grpc_err:
+ raise exception_from_grpc_error(grpc_err)
+
+ def _to_resource(self) -> Resource:
+ return Resource(name=self.name, type=ResourceType.Topic)
+
+ def _perms_to_actions(self, *args: Union[TopicPermission, str]) -> List[Action]:
+ _permMap = {TopicPermission.publishing: [Action.TopicEventPublish]}
+ # convert strings to the enum value where needed
+ perms = [
+ permission if isinstance(permission, TopicPermission) else TopicPermission[permission.lower()]
+ for permission in args
+ ]
+
+ return [action for perm in perms for action in _permMap[perm]]
+
+ def allow(self, *args: Union[TopicPermission, str]) -> TopicRef:
+ """Request the specified permissions to this resource."""
+ self._register_policy(*args)
+
+ return Events().topic(self.name)
+
+ def subscribe(self, func: EventMiddleware):
+ """Create and return a subscription decorator for this topic."""
+ self.server = FunctionServer(SubscriptionWorkerOptions(topic=self.name))
+ self.server.event(func)
+ Nitric._register_worker(self.server)