Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
116 changes: 116 additions & 0 deletions async.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
asyncapi: 3.0.0
channels:
notify-air-quality:
address: messages/a/data/collections/air-quality
description: Fraunhofer IOSB Air Quality
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
notify-canada-metadata:
address: messages/a/data/collections/canada-metadata
description: Open Canada sample data
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
notify-icoads-sst:
address: messages/a/data/collections/icoads-sst
description: International Comprehensive Ocean-Atmosphere Data Set (ICOADS)
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
notify-lakes:
address: messages/a/data/collections/lakes
description: Large Lakes
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
notify-mapserver_world_map:
address: messages/a/data/collections/mapserver_world_map
description: MapServer demo WMS world map
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
notify-obs:
address: messages/a/data/collections/obs
description: Observations
messages:
DefaultMessage:
payload:
$ref: https://raw.githubusercontent.com/wmo-im/wis2-monitoring-events/refs/heads/main/schemas/cloudevents-v1.0.2.yaml
defaultContentType: application/json
id: http://localhost:5002
info:
contact:
email: you@example.org
name: Lastname, Firstname
description: pygeoapi provides an API to geospatial data
externalDocs:
url: https://example.org
license:
name: CC-BY 4.0 license
url: https://creativecommons.org/licenses/by/4.0/
tags:
- name: geospatial
- name: data
- name: api
title: pygeoapi default instance
version: 0.24.dev0
operations:
consume-air-quality:
action: receive
channel:
$ref: '#/channels/notify-air-quality'
consume-canada-metadata:
action: receive
channel:
$ref: '#/channels/notify-canada-metadata'
consume-icoads-sst:
action: receive
channel:
$ref: '#/channels/notify-icoads-sst'
consume-lakes:
action: receive
channel:
$ref: '#/channels/notify-lakes'
consume-mapserver_world_map:
action: receive
channel:
$ref: '#/channels/notify-mapserver_world_map'
consume-obs:
action: receive
channel:
$ref: '#/channels/notify-obs'
publish-air-quality:
action: send
channel:
$ref: '#/channels/notify-air-quality'
publish-canada-metadata:
action: send
channel:
$ref: '#/channels/notify-canada-metadata'
publish-icoads-sst:
action: send
channel:
$ref: '#/channels/notify-icoads-sst'
publish-lakes:
action: send
channel:
$ref: '#/channels/notify-lakes'
publish-mapserver_world_map:
action: send
channel:
$ref: '#/channels/notify-mapserver_world_map'
publish-obs:
action: send
channel:
$ref: '#/channels/notify-obs'
servers:
default:
description: pygeoapi provides an API to geospatial data
host: localhost:1883
protocol: mqtt
4 changes: 2 additions & 2 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ cd ${PYGEOAPI_HOME}
echo "Default config in ${PYGEOAPI_CONFIG}"

echo "Trying to generate openapi.yml"
/venv/bin/pygeoapi openapi generate ${PYGEOAPI_CONFIG} --output-file ${PYGEOAPI_OPENAPI} ${OPENAPI_GENERATE_FAIL_ON_INVALID_COLLECTION}
/venv/bin/pygeoapi openapi generate ${OPENAPI_GENERATE_FAIL_ON_INVALID_COLLECTION}

[[ $? -ne 0 ]] && error "openapi.yml could not be generated ERROR"

echo "openapi.yml generated continue to pygeoapi"

echo "Trying to generate asyncapi.yml"
/venv/bin/pygeoapi asyncapi generate ${PYGEOAPI_CONFIG} --output-file ${PYGEOAPI_ASYNCAPI}
/venv/bin/pygeoapi asyncapi generate

[[ $? -ne 0 ]] && echo "asyncapi.yml could not be generated; skipping"

Expand Down
6 changes: 3 additions & 3 deletions docs/source/administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ To generate the OpenAPI document, run the following:

.. code-block:: bash

pygeoapi openapi generate /path/to/my-pygeoapi-config.yml
pygeoapi openapi generate /path/to/my-pygeoapi-config.yml -f yaml

This will dump the OpenAPI document as YAML to your system's ``stdout``. To save to a file on disk, run:

.. code-block:: bash

pygeoapi openapi generate /path/to/my-pygeoapi-config.yml --output-file /path/to/my-pygeoapi-openapi.yml
pygeoapi openapi generate /path/to/my-pygeoapi-config.yml --openapi-file /path/to/my-pygeoapi-openapi.yml

To generate the OpenAPI document as JSON, run:

.. code-block:: bash

pygeoapi openapi generate /path/to/my-pygeoapi-config.yml --format json --output-file /path/to/my-pygeoapi-openapi.json
pygeoapi openapi generate /path/to/my-pygeoapi-config.yml --openapi-file /path/to/my-pygeoapi-openapi.json

.. note::
Generate as YAML or JSON? If your OpenAPI YAML definition is slow to render as JSON,
Expand Down
8 changes: 4 additions & 4 deletions docs/source/pubsub.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ To generate the AsyncAPI document, run the following:

.. code-block:: bash

pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml
pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml -f json

This will dump the AsyncAPI document as YAML to your system's ``stdout``. To save to a file on disk, run:
This will dump the AsyncAPI document as YAML to your system's ``stdout`` as JSON. To save to a file on disk, run:

.. code-block:: bash

pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml --output-file /path/to/my-pygeoapi-asyncapi.yml
pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml --asyncapi-file /path/to/my-pygeoapi-asyncapi.yml

To generate the AsyncAPI document as JSON, run:

.. code-block:: bash

pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml --format json --output-file /path/to/my-pygeoapi-asyncapi.json
pygeoapi asyncapi generate /path/to/my-pygeoapi-config.yml --asyncapi-file /path/to/my-pygeoapi-asyncapi.json

.. note::
Generate as YAML or JSON? If your AsyncAPI YAML definition is slow to render as JSON,
Expand Down
6 changes: 3 additions & 3 deletions pygeoapi/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from jsonschema.exceptions import ValidationError

from pygeoapi.api import API, APIRequest
from pygeoapi.config import get_config, validate_config
from pygeoapi.config import get_config, validate_config_document
from pygeoapi.formats import F_HTML
from pygeoapi.openapi import get_oas
from pygeoapi.util import to_json, render_j2_template, yaml_dump
Expand Down Expand Up @@ -99,7 +99,7 @@ def validate(self, config: dict):

# validate pygeoapi configuration
LOGGER.debug('Validating configuration')
validate_config(config)
validate_config_document(config)
# validate OpenAPI document
# LOGGER.debug('Validating openapi document')
# oas = get_oas(config)
Expand All @@ -125,7 +125,7 @@ def write_config(self, config: dict):

# validate pygeoapi configuration
config = deepcopy(config)
validate_config(config)
validate_config_document(config)

# Preserve env variables
LOGGER.debug('Reading env variables in configuration')
Expand Down
109 changes: 55 additions & 54 deletions pygeoapi/asyncapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
import yaml

from pygeoapi import __version__, l10n
from pygeoapi.config import get_config, cli_config
from pygeoapi.models.openapi import OAPIFormat
from pygeoapi.util import to_json, yaml_load, remove_url_auth
from pygeoapi.util import to_json, yaml_load, remove_url_auth, SCHEMASDIR

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -150,20 +151,13 @@ def gen_asyncapi(cfg: dict) -> dict:
return a


def get_asyncapi(cfg, version='3.0'):
"""
Stub to generate AsyncAPI Document
def get_asyncapi_schema() -> dict:
"""Reads the asyncapi JSON schema file."""

:param cfg: configuration object
:param version: version of AsyncAPI (default 3.0)
schema_file = SCHEMASDIR / 'asyncapi' / 'asyncapi-3.0.0.json'

:returns: AsyncAPI definition YAML dict
"""

if version == '3.0':
return gen_asyncapi(cfg)
else:
raise RuntimeError('AsyncAPI version not supported')
with schema_file.open() as fh:
return json.load(fh)


def validate_asyncapi_document(instance_dict):
Expand All @@ -175,15 +169,9 @@ def validate_asyncapi_document(instance_dict):
:returns: `bool` of validation
"""

schema_file = os.path.join(
THISDIR, 'resources', 'schemas', 'asyncapi', 'asyncapi-3.0.0.json')

LOGGER.debug(f'Validating against {schema_file}')
with open(schema_file) as fh2:
schema_dict = json.load(fh2)
jsonschema_validate(instance_dict, schema_dict)
jsonschema_validate(instance_dict, get_asyncapi_schema())

return True
return True


def generate_asyncapi_document(cfg: dict, output_format: OAPIFormat):
Expand All @@ -199,34 +187,38 @@ def generate_asyncapi_document(cfg: dict, output_format: OAPIFormat):

pretty_print = cfg['server'].get('pretty_print', False)

if output_format == 'yaml':
content = yaml.safe_dump(get_asyncapi(cfg), default_flow_style=False)
if output_format.endswith(('yaml', 'yml')):
content = yaml.safe_dump(gen_asyncapi(cfg), default_flow_style=False)
else:
content = to_json(get_asyncapi(cfg), pretty=pretty_print)
content = to_json(gen_asyncapi(cfg), pretty=pretty_print)
return content


def load_asyncapi_document() -> dict:
def get_asyncapi(file_path: str | None = None) -> dict:
"""
Open AsyncAPI document from `PYGEOAPI_ASYNCAPI` environment variable
Read pygeoapi AsyncAPI document

:returns: `dict` of AsyncAPI document
:param file_path: `str` of path to configuration file; if `None`,
reads from `PYGEOAPI_ASYNCAPI` environment variable

:returns: `dict` of OpenAPI document
"""

pygeoapi_asyncapi = os.environ.get('PYGEOAPI_ASYNCAPI')
if file_path is None:
file_path = os.environ.get('PYGEOAPI_ASYNCAPI')

if pygeoapi_asyncapi is None:
if not file_path:
LOGGER.debug('PYGEOAPI_ASYNCAPI environment not set')
return {}

if not os.path.exists(pygeoapi_asyncapi):
msg = (f'AsyncAPI document {pygeoapi_asyncapi} does not exist. '
if not os.path.exists(file_path):
msg = (f'AsyncAPI document {file_path} does not exist. '
'Please generate before starting pygeoapi')
LOGGER.warning(msg)
return {}
LOGGER.error(msg)
raise RuntimeError(msg)

with open(pygeoapi_asyncapi, encoding='utf8') as ff:
if pygeoapi_asyncapi.endswith(('.yaml', '.yml')):
with open(file_path, encoding='utf8') as ff:
if file_path.endswith(('yaml', 'yml')):
asyncapi_ = yaml_load(ff)
else: # JSON string, do not transform
asyncapi_ = ff.read()
Expand All @@ -242,50 +234,59 @@ def asyncapi():

@click.command()
@click.pass_context
@click.argument('config_file', type=click.File(encoding='utf-8'))
@cli_config
@click.option(
'--asyncapi-file',
'-af',
'asyncapi_file',
type=click.File('w'),
envvar='PYGEOAPI_ASYNCAPI',
help='Name of asyncapi file (env: PYGEOAPI_ASYNCAPI)'
)
@click.option('--output-file', 'deprecated', type=click.File('w'), hidden=True)
@click.option('--format', '-f', 'format_', type=click.Choice(['json', 'yaml']),
default='yaml', help='output format (json|yaml)')
@click.option('--output-file', '-of', type=click.File('w', encoding='utf-8'),
help='Name of output file')
def generate(ctx, config_file, output_file, format_='yaml'):
help='output format (json|yaml); only applies to stdout.')
def generate(ctx, config_file, asyncapi_file, deprecated, format_):
"""Generate AsyncAPI Document"""

if config_file is None:
raise click.ClickException('--config/-c required')
if deprecated is not None:
click.echo(
'Warning: --output-file is deprecated; use --asyncapi-file',
err=True,
)
if asyncapi_file is None:
asyncapi_file = deprecated

if isinstance(config_file, Path):
with config_file.open(mode='r') as cf:
cfg = yaml_load(cf)
else:
cfg = yaml_load(config_file)
cfg = get_config(config_file)

if 'pubsub' not in cfg:
click.echo('pubsub not configured; aborting')
ctx.exit(1)

format_ = Path(asyncapi_file.name).suffix if asyncapi_file else format_
content = generate_asyncapi_document(cfg, format_)

if output_file is None:
if asyncapi_file is None:
click.echo(content)
else:
click.echo(f'Generating {output_file.name}')
output_file.write(content)
click.echo(f'Generating {asyncapi_file.name}')
asyncapi_file.write(content)
click.echo('Done')


@click.command()
@click.pass_context
@click.argument('asyncapi_file', type=click.File())
@click.argument('asyncapi_file', type=click.File(), envvar='PYGEOAPI_ASYNCAPI')
def validate(ctx, asyncapi_file):
"""Validate AsyncAPI Document"""

if asyncapi_file is None:
raise click.ClickException('--asyncapi/-o required')
raise click.ClickException('asyncapi file required')

click.echo(f'Validating {asyncapi_file.name}')
instance = yaml_load(asyncapi_file)
validate_asyncapi_document(instance)
click.echo('Valid AsyncAPI document')
if validate_asyncapi_document(instance):
click.echo('Valid AsyncAPI document')


asyncapi.add_command(generate)
Expand Down
Loading
Loading