Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish answers to question topic #603

Merged
merged 125 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
7255d2f
ENH: Reply to questions on question topic instead of answer topic
cortadocodes Jul 25, 2023
54e69b6
ENH: Add question UUID attribute to all answer messages
cortadocodes Jul 25, 2023
8b5d01f
ENH: Add ability to filter subscriptions
cortadocodes Jul 25, 2023
e84b022
ENH: Filter answer subscriptions on question UUID
cortadocodes Jul 25, 2023
bf2f772
ENH: Filter question subscriptions on `is_question` attribute
cortadocodes Jul 25, 2023
0b43dc9
FIX: Filter out questions from answer subscription
cortadocodes Jul 25, 2023
40c5148
FIX: Create separate answer subscription
cortadocodes Jul 25, 2023
158cf00
ENH: Update Pub/Sub mocks to deal with combined question/answer topics
cortadocodes Jul 25, 2023
ef7a91a
TST: Fix test
cortadocodes Jul 26, 2023
159ea65
MRG: Merge remote-tracking branch 'origin/main' into publish-answers-…
cortadocodes Jul 27, 2023
2042fb6
TST: Store pub/sub messages against subscriptions in mocks
cortadocodes Jul 27, 2023
42c466b
FIX: Get question UUID from subscription path in message handler
cortadocodes Jul 27, 2023
1c5766f
FIX: Add question UUID to log record messages in `GooglePubSubHandler`
cortadocodes Jul 27, 2023
6f6771c
ENH: Add representation to `MockMessage`
cortadocodes Jul 27, 2023
ff610f4
TST: Fix `GooglePubSubHandler` test
cortadocodes Jul 27, 2023
f8c27db
FIX: Detach message number from `Topic` and define in `Service.answer`
cortadocodes Jul 27, 2023
e0c4c35
DEP: Update requirements of template apps
cortadocodes Jul 27, 2023
c224616
OPS: Increase minor version number
cortadocodes Jul 27, 2023
b9559d9
FIX: Track messages sent in topic and stop sharing topics in service
cortadocodes Jul 27, 2023
ad757c8
TST: Update deployment test
cortadocodes Jul 27, 2023
e375041
FIX: Add missing "is_question" attribute to log record messages
cortadocodes Jul 27, 2023
b3bd877
ENH: Store message number in message attributes
cortadocodes Jul 31, 2023
7d3010e
ENH: Remove heartbeat backwards compatibility
cortadocodes Jul 31, 2023
e33e529
ENH: Remove backwards compatibility with old manifest serialisation
cortadocodes Jul 31, 2023
6484ba7
REF: Make message attributes in `Service._send_message` explicit
cortadocodes Jul 31, 2023
e191c33
REF: Rename `analysis_id` to `question_uuid` in `GooglePubSubHandler`
cortadocodes Jul 31, 2023
bd9e778
REF: Rename `analysis_id` field to `question_uuid` in log messages
cortadocodes Jul 31, 2023
9008cc1
REF: Rename `_waiting_messages` to `waiting_messages`
cortadocodes Jul 31, 2023
a782ebf
ENH: Expect all question metadata to be present as message attributes
cortadocodes Jul 31, 2023
d0148cc
FIX: Convert question attribute to boolean
cortadocodes Jul 31, 2023
d4ac4d3
ENH: Publish more possible errors to topic in `Service.answer`
cortadocodes Jul 31, 2023
f752390
ENH: Remove question UUID from log record message body
cortadocodes Jul 31, 2023
d24e6bd
REF: Make attributes of `MockMessage` explicit
cortadocodes Jul 31, 2023
2985dcc
OPS: Fix workflow
cortadocodes Nov 13, 2023
c3ccdfe
ENH: Log entire unknown messages if received
cortadocodes Nov 13, 2023
598e1fa
DEP: Update lock file
cortadocodes Nov 14, 2023
6ee5f47
ENH: Validate messages received in message handler against schema
cortadocodes Nov 14, 2023
c0c5968
FIX: Use fixed message schema and fix dealing with invalid messages
cortadocodes Nov 14, 2023
942d536
TST: Update tests messages to comply with service communication schema
cortadocodes Nov 14, 2023
0b53c6c
ENH: Validate questions received by child against JSON schema
cortadocodes Nov 14, 2023
c400a42
REF: Simplify `Child.ask_multiple`
cortadocodes Nov 14, 2023
05d7968
ENH: Use message schema that uses twined children schema
cortadocodes Nov 14, 2023
de30a22
ENH: Use message schema that uses twined manifest schema
cortadocodes Nov 14, 2023
0148756
FIX: Use message schema fixed for output manifests
cortadocodes Nov 15, 2023
d3b5a24
REV: Undo test updates enforced by over-specific schema
cortadocodes Nov 15, 2023
5e77186
REF: Rename and simplify `warn_of_or_raise_invalid_message_error`
cortadocodes Nov 15, 2023
e5e40d4
ENH: Validate message attributes
cortadocodes Nov 15, 2023
23a9a58
REF: Factor out more validation logic
cortadocodes Nov 15, 2023
e0182b9
OPS: Add readthedocs config
cortadocodes Nov 15, 2023
bcf76f0
MRG: Merge branch 'main' into publish-answers-to-question-topic-2
cortadocodes Nov 15, 2023
63b884c
OPS: Add missing permissions to test publish job
cortadocodes Nov 15, 2023
aed91cf
ENH: Add `type` field to question messages
cortadocodes Nov 15, 2023
b0dfce4
FIX: Stop double-JSON-encoding output manifests
cortadocodes Nov 15, 2023
8118bbb
FIX: Add missing `type` field to emulated Pub/Sub questions
cortadocodes Nov 15, 2023
250dcd1
ENH: Make input and output values and manifest optional
cortadocodes Nov 15, 2023
e9ec70d
ENH: Add default schema for `is_message_valid`
cortadocodes Nov 20, 2023
312673d
ENH: Validate boolean values instead of strings "0" and "1"
cortadocodes Nov 20, 2023
f5078dc
FIX: Allow empty schema in `is_message_valid`
cortadocodes Nov 20, 2023
aea8130
FIX: Ensure message attributes are in a dictionary
cortadocodes Nov 20, 2023
5695f40
DEP: Update `octue` version used in template apps
cortadocodes Nov 20, 2023
29b2b5c
ENH: Improve message validation methods
cortadocodes Nov 20, 2023
fae8b73
REF: Move `validation` module up one level
cortadocodes Nov 20, 2023
d5de971
REF: Move `log_invalid_message` inside `raise_if_message_is_invalid`
cortadocodes Nov 20, 2023
6b86478
MRG: Merge branch 'add-service-communication-schema' into publish-ans…
cortadocodes Nov 20, 2023
67b0849
ENH: Use new service communication schema
cortadocodes Nov 21, 2023
232bb35
REF: Rename functions to use "event" instead of "message"
cortadocodes Nov 21, 2023
3e6a51b
OPS: Increase minor version number
cortadocodes Nov 21, 2023
c94cccc
REF: Rename `is_question` to `sender_type` and change type to `enum`
cortadocodes Nov 21, 2023
49b52e3
ENH: Enable debugging to always be switched on for a service
cortadocodes Nov 22, 2023
f8966ef
REF: Simplify `extract_event_and_attributes_from_pub_sub`
cortadocodes Nov 22, 2023
72bbea8
TST: Update cloud deployment test
cortadocodes Nov 22, 2023
264f6cb
OPS: Stop building docker images for registry
cortadocodes Nov 22, 2023
2042346
DOC: Update `Service.ask` argument name in docs
cortadocodes Nov 22, 2023
c03392b
DOC: Document service communication schema
cortadocodes Nov 22, 2023
2aace2a
CHO: Remove version compatibility data below version `0.30.0`
cortadocodes Nov 22, 2023
a10b4ba
DOC: Add docstring
cortadocodes Nov 22, 2023
0787da8
REF: Rename `OrderedMessageHandler.schema` attribute
cortadocodes Nov 22, 2023
1a2b999
FIX: Avoid using attribute until validated in message handler
cortadocodes Nov 22, 2023
5f2bc48
FIX: Send configuration exceptions to parent
cortadocodes Nov 22, 2023
2309e79
REF: Remove unnecessary `get`
cortadocodes Nov 22, 2023
a5a6c26
TST: Update compatibility tests
cortadocodes Nov 22, 2023
c79a87c
DOC: Improve `Subscription` docstring
cortadocodes Nov 22, 2023
cd64952
DOC: Improve documentation of message arguments
cortadocodes Nov 22, 2023
5eda178
REF: Rename `messages` module to `events`
cortadocodes Nov 22, 2023
3f23d91
ENH: Include schema version in invalid event error
cortadocodes Nov 22, 2023
965896c
REF: Simplify child emulator and its tests
cortadocodes Nov 22, 2023
a640a8a
REF: Factor out sender types
cortadocodes Nov 22, 2023
af1e936
FIX: Stop missing parent SDK version stopping event validation
cortadocodes Nov 22, 2023
04b8755
DOC: Improve docstring
cortadocodes Nov 23, 2023
3648e7e
REF: Simplify `MockMessage` instantiation
cortadocodes Nov 23, 2023
1dfb377
DOC: Add missing end of sentence in docs
cortadocodes Nov 23, 2023
2c05836
ENH: Use uppercase values for sender type attribute
cortadocodes Nov 23, 2023
32cd805
CHO: Add version compatibility metadata
cortadocodes Nov 23, 2023
a17087d
REF: Rename "octue_sdk_version" to "version"
cortadocodes Nov 23, 2023
157764f
REF: Rename "time" message keys to "datetime"
cortadocodes Nov 23, 2023
dc6355a
ENH: Use RFC3339-compliant UTC time for timestamps
cortadocodes Nov 23, 2023
bce332a
REF: Rename message "type" to "kind"
cortadocodes Nov 23, 2023
551747f
REF: Rename "traceback" key to "exception_traceback"
cortadocodes Nov 23, 2023
ee3dd36
REF: Rename "debug" to "save_diagnostics"
cortadocodes Nov 23, 2023
03616d1
FIX: Stop double-JSON-encoding monitor messages
cortadocodes Nov 23, 2023
a276497
TST: Update service tests
cortadocodes Nov 23, 2023
dded15c
ENH: Use updated schema
cortadocodes Nov 23, 2023
93697d7
TST: Fix tests
cortadocodes Nov 23, 2023
2198a2b
FIX: Fix child emulator message keys
cortadocodes Nov 23, 2023
d8e0b43
FIX: Use fixed schema
cortadocodes Nov 23, 2023
5565232
OPS: Use correct GitHub token in workflow
cortadocodes Nov 23, 2023
7a14876
FIX: Stop trying to JSON-decode input manifest dictionary
cortadocodes Nov 27, 2023
027a3ec
TST: Fix monitor message format in child emulator file tests
cortadocodes Nov 27, 2023
be8e06e
ENH: Log service revision found in registry in `get_default_sruid`
cortadocodes Nov 28, 2023
3fb86b8
ENH: Add ability to instantiate `Runner` from service/app configurations
cortadocodes Nov 28, 2023
eff159d
FIX: Use absolute paths where necessary in `ServiceConfiguration`
cortadocodes Nov 28, 2023
028e811
REF: Use new `Runner` constructor in `octue start`
cortadocodes Nov 28, 2023
788556f
TST: Update CLI tests
cortadocodes Nov 28, 2023
3bebcf6
FIX: Ensure paths specified in service config are relative to it
cortadocodes Nov 28, 2023
7fdb64b
FIX: Remove extraneous colon from log message
cortadocodes Dec 4, 2023
eaf710f
MRG: Merge branch 'main' into publish-answers-to-question-topic-2
cortadocodes Jan 9, 2024
214fc97
TST: Permit use of path endings in `MockOpen`
cortadocodes Jan 9, 2024
da92532
FIX: Stop `.` being appended to directory in `ServiceConfiguration`
cortadocodes Jan 9, 2024
53540c3
TST: Update config tests to work with absolute paths
cortadocodes Jan 9, 2024
fc3a00e
FIX: Fix input manifest in `ChildEmulator._handle_result`
cortadocodes Jan 9, 2024
04364f2
TST: Update child emulator test
cortadocodes Jan 9, 2024
bbcdb64
TST: Fix path in test for Windows
cortadocodes Jan 9, 2024
4ecf106
DOC: Update parameter name in docs
cortadocodes Jan 9, 2024
f7a9bb9
REF: Rename crash diagnostics to diagnostics
cortadocodes Jan 9, 2024
1902194
DOC: Fix documentation
cortadocodes Jan 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/add-issues-to-octue-board.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:

jobs:
add-issues-to-octue-board:
uses: octue/.github/.github/workflows/reusable-add-issues-to-octue-board.yml@main
secrets:
github-token: ${{ secrets.PROJECT_AUTOMATION_GITHUB_TOKEN_2 }}
runs-on: ubuntu-latest
steps:
- name: Add to Board
uses: actions/[email protected]
with:
project-url: https://github.com/orgs/octue/projects/22
github-token: ${{ secrets.OCTUE_PROJECT_ISSUES_TOKEN }}
3 changes: 3 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ jobs:
if: "!contains(github.event.head_commit.message, 'skipci')"
runs-on: ubuntu-latest
needs: [check-semantic-version, run-tests]
permissions:
id-token: write
contents: read
steps:
- name: Checkout Repository
uses: actions/checkout@v3
Expand Down
21 changes: 0 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,3 @@ jobs:

- name: Publish package distributions to PyPI
uses: pypa/[email protected]

docker:
runs-on: ubuntu-latest
needs: [run-tests, release]
timeout-minutes: 300
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Log in to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push
uses: docker/[email protected]
with:
context: .
push: true
tags: octue/octue-sdk-python:${{ needs.run-tests.outputs.package_version }}-slim,octue/octue-sdk-python:latest
16 changes: 16 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
os: ubuntu-22.04
tools:
python: "3.10"

sphinx:
configuration: docs/source/conf.py

python:
install:
- requirements: docs/requirements.txt
2 changes: 1 addition & 1 deletion docs/source/asking_questions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ You can also set the following options when you call :mod:`Child.ask <octue.reso
- ``allow_local_files`` - if true, local files/datasets are allowed in any input manifest you supply
- ``handle_monitor_message`` - if provided a function, it will be called on any monitor messages from the child
- ``record_messages_to`` – if given a path to a JSON file, messages received from the parent while it processes the question are saved to it
- ``allow_save_diagnostics_data_on_crash`` – if true, the input values and input manifest (including its datasets) will be saved by the child for future crash diagnostics if it fails while processing them
- ``save_diagnostics`` – must be one of {"SAVE_DIAGNOSTICS_OFF", "SAVE_DIAGNOSTICS_ON_CRASH", "SAVE_DIAGNOSTICS_ON"}; if turned on, allow the input values and manifest (and its datasets) to be saved by the child either all the time or just if the analysis fails
- ``question_uuid`` - if provided, the question will use this UUID instead of a generated one
- ``timeout`` - how long in seconds to wait for an answer (``None`` by default - i.e. don't time out)

Expand Down
271 changes: 97 additions & 174 deletions docs/source/inter_service_compatibility.rst

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions docs/source/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,12 @@ They look like ``namespace/name:tag`` where the tag is often a semantic version
name. It can be used to ask a question to a service without specifying a specific revision of it. This enables
asking questions to, for example, the service ``octue/my-service`` and automatically having them routed to its
default (usually latest) revision. :ref:`See here for more info<using_default_revision_tag>`.


Service communication standard
==============================

Octue services communicate according to the service communication standard. The JSON schema defining this can be found
`here <https://strands.octue.com/octue/service-communication>`_. Messages received by services are validated against it
and invalid messages are rejected. The schema is in beta, so (rare) breaking changes are reflected in the minor version
number.
6 changes: 3 additions & 3 deletions docs/source/troubleshooting_services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Services save the following data to the cloud if they crash while processing a q

.. important::

For this feature to be enabled, the child must have the ``crash_diagnostics_cloud_path`` field in its service
For this feature to be enabled, the child must have the ``diagnostics_cloud_path`` field in its service
configuration (:ref:`octue.yaml <octue_yaml>` file) set to a Google Cloud Storage path.


Expand Down Expand Up @@ -111,7 +111,7 @@ your service to fail.
Disabling crash diagnostics
===========================
When asking a question to a child, parents can disable crash diagnostics upload in the child on a question-by-question
basis by setting ``allow_save_diagnostics_data_on_crash`` to ``False`` in :mod:`Child.ask <octue.resources.child.Child.ask>`.
basis by setting ``save_diagnostics`` to ``"SAVE_DIAGNOSTICS_OFF"`` in :mod:`Child.ask <octue.resources.child.Child.ask>`.
For example:

.. code-block:: python
Expand All @@ -123,5 +123,5 @@ For example:

answer = child.ask(
input_values={"height": 32, "width": 3},
allow_save_diagnostics_data_on_crash=False,
save_diagnostics="SAVE_DIAGNOSTICS_OFF",
)
1 change: 1 addition & 0 deletions octue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


__all__ = ("Runner",)

REPOSITORY_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))


Expand Down
36 changes: 11 additions & 25 deletions octue/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from octue.cloud import storage
from octue.cloud.pub_sub import Subscription, Topic
from octue.cloud.pub_sub.service import Service
from octue.cloud.pub_sub.service import PARENT_SENDER_TYPE, Service
from octue.cloud.service_id import convert_service_id_to_pub_sub_form, create_sruid, get_sruid_parts
from octue.cloud.storage import GoogleCloudStorageClient
from octue.configuration import load_service_and_app_configuration
Expand All @@ -22,7 +22,6 @@
from octue.resources import Manifest, service_backends
from octue.runner import Runner
from octue.utils.encoders import OctueJSONEncoder
from twined import Twine


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -129,16 +128,7 @@ def run(service_config, input_dir, output_file, output_manifest_file, monitor_me
if os.path.exists(input_manifest_path):
input_manifest = input_manifest_path

runner = Runner(
app_src=service_configuration.app_source_path,
twine=Twine(source=service_configuration.twine_path),
configuration_values=app_configuration.configuration_values,
configuration_manifest=app_configuration.configuration_manifest,
children=app_configuration.children,
output_location=app_configuration.output_location,
crash_diagnostics_cloud_path=service_configuration.crash_diagnostics_cloud_path,
service_registries=service_configuration.service_registries,
)
runner = Runner.from_configuration(service_configuration=service_configuration, app_configuration=app_configuration)

if monitor_messages_file:
if not os.path.exists(os.path.dirname(monitor_messages_file)):
Expand Down Expand Up @@ -229,14 +219,9 @@ def start(service_config, revision_tag, timeout, no_rm):
revision_tag=service_revision_tag_override or service_revision_tag,
)

runner = Runner(
app_src=service_configuration.app_source_path,
twine=Twine(source=service_configuration.twine_path),
configuration_values=app_configuration.configuration_values,
configuration_manifest=app_configuration.configuration_manifest,
children=app_configuration.children,
output_location=app_configuration.output_location,
crash_diagnostics_cloud_path=service_configuration.crash_diagnostics_cloud_path,
runner = Runner.from_configuration(
service_configuration=service_configuration,
app_configuration=app_configuration,
service_id=service_sruid,
)

Expand Down Expand Up @@ -296,12 +281,12 @@ def start(service_config, revision_tag, timeout, no_rm):
@click.option(
"--download-datasets",
is_flag=True,
help="If provided, download any datasets from the crash diagnostics and update their paths in the configuration and "
help="If provided, download any datasets from the diagnostics and update their paths in the configuration and "
"input manifests to the new local paths.",
)
def get_crash_diagnostics(cloud_path, local_path, download_datasets):
"""Download crash diagnostics for an analysis from the given directory in Google Cloud Storage. The cloud path
should end in the analysis ID.
def get_diagnostics(cloud_path, local_path, download_datasets):
"""Download diagnostics for a question from the given directory in Google Cloud Storage. The cloud path should end
in the question ID.

CLOUD_PATH: The path to the directory in Google Cloud Storage containing the diagnostics data.
"""
Expand Down Expand Up @@ -344,7 +329,7 @@ def get_crash_diagnostics(cloud_path, local_path, download_datasets):

manifest.to_file(manifest_path)

logger.info("Downloaded crash diagnostics from %r to %r.", cloud_path, local_path)
logger.info("Downloaded diagnostics from %r to %r.", cloud_path, local_path)


@octue_cli.group()
Expand Down Expand Up @@ -409,6 +394,7 @@ def create_push_subscription(
name=pub_sub_sruid,
topic=topic,
project_name=project_name,
filter=f'attributes.sender_type = "{PARENT_SENDER_TYPE}"',
expiration_time=expiration_time,
push_endpoint=push_endpoint,
)
Expand Down
29 changes: 12 additions & 17 deletions octue/cloud/deployment/google/answer_pub_sub_question.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging

from octue.cloud.pub_sub import Topic
from octue.cloud.pub_sub.service import Service
from octue.cloud.service_id import create_sruid, get_sruid_parts
from octue.cloud.service_id import convert_service_id_to_pub_sub_form, create_sruid, get_sruid_parts
from octue.configuration import load_service_and_app_configuration
from octue.resources.service_backends import GCPPubSubBackend
from octue.runner import Runner
Expand All @@ -17,7 +18,7 @@
def answer_question(question, project_name):
"""Answer a question sent to an app deployed in Google Cloud.

:param dict|tuple|apache_beam.io.gcp.pubsub.PubsubMessage question:
:param dict|tuple question:
:param str project_name:
:return None:
"""
Expand All @@ -31,30 +32,24 @@ def answer_question(question, project_name):
)

service = Service(service_id=service_sruid, backend=GCPPubSubBackend(project_name=project_name))

question_uuid = get_nested_attribute(question, "attributes.question_uuid")
answer_topic = service.instantiate_answer_topic(question_uuid)

try:
runner = Runner(
app_src=service_configuration.app_source_path,
twine=service_configuration.twine_path,
configuration_values=app_configuration.configuration_values,
configuration_manifest=app_configuration.configuration_manifest,
children=app_configuration.children,
output_location=app_configuration.output_location,
crash_diagnostics_cloud_path=service_configuration.crash_diagnostics_cloud_path,
runner = Runner.from_configuration(
service_configuration=service_configuration,
app_configuration=app_configuration,
project_name=project_name,
service_id=service_sruid,
service_registries=service_configuration.service_registries,
)

service.run_function = runner.run

service.answer(question, answer_topic=answer_topic)
service.answer(question)
logger.info("Analysis successfully run and response sent for question %r.", question_uuid)

# Forward any errors in the deployment configuration (errors in the analysis are already forwarded by the service).
except BaseException as error: # noqa
service.send_exception(topic=answer_topic)
service.send_exception(
topic=Topic(name=convert_service_id_to_pub_sub_form(service_sruid), project_name=project_name),
question_uuid=question_uuid,
)

logger.exception(error)
Loading
Loading