Skip to content

Commit

Permalink
add support for AsyncAPI (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkralidis authored Sep 30, 2022
1 parent c1b8f41 commit 4fdcae9
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 10 deletions.
2 changes: 1 addition & 1 deletion docker/pygeoapi-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ metadata:
phone: +xx-xxx-xxx-xxxx
fax: +xx-xxx-xxx-xxxx
email: you@example.org
url: Contact URL
url: https://example.org
hours: Mo-Fr 08:00-17:00
instructions: During hours of service. Off on weekends.
role: pointOfContact
Expand Down
1 change: 1 addition & 0 deletions wis2box.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export WIS2BOX_API_URL=http://localhost:5001
export WIS2BOX_DATAPATH=/path/to/wis2box-data
export WIS2BOX_MQTT_URL=http://localhost:1883
14 changes: 7 additions & 7 deletions wis2box_api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def admin(self, request: Union[APIRequest, Any]) -> Tuple[dict, int, str]:
"""
Provide admin document
:param request: A request object
:param request: request object
:returns: tuple of headers, status code, content
"""
Expand All @@ -172,7 +172,7 @@ def resources(
"""
Provide admin document
:param request: A request object
:param request: request object
:returns: tuple of headers, status code, content
"""
Expand Down Expand Up @@ -201,7 +201,7 @@ def post_resource(
"""
Add resource configuration
:param request: A request object
:param request: request object
:returns: tuple of headers, status code, content
"""
Expand Down Expand Up @@ -271,7 +271,7 @@ def get_resource(
"""
Get resource configuration
:param request: A request object
:param request: request object
:param resource_id:
:returns: tuple of headers, status code, content
Expand Down Expand Up @@ -306,7 +306,7 @@ def delete_resource(
"""
Delete resource configuration
:param request: A request object
:param request: request object
:param resource_id: resource identifier
:returns: tuple of headers, status code, content
Expand Down Expand Up @@ -350,7 +350,7 @@ def put_resource(
"""
Update complete resource configuration
:param request: A request object
:param request: request object
:param resource_id: resource identifier
:returns: tuple of headers, status code, content
Expand Down Expand Up @@ -417,7 +417,7 @@ def patch_resource(
"""
Update partial resource configuration
:param request: A request object
:param request: request object
:param resource_id: resource identifier
:returns: tuple of headers, status code, content
Expand Down
4 changes: 2 additions & 2 deletions wis2box_api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@
###############################################################################

from flask import Flask, redirect

from pygeoapi.flask_app import BLUEPRINT as pygeoapi_blueprint

from wis2box_api.flask_admin import ADMIN_BLUEPRINT
from wis2box_api.flask_asyncapi import ASYNCAPI_BLUEPRINT

app = Flask(__name__, static_url_path='/static')
app.url_map.strict_slashes = False

app.register_blueprint(ASYNCAPI_BLUEPRINT, url_prefix='/asyncapi')
app.register_blueprint(pygeoapi_blueprint, url_prefix='/oapi')
app.register_blueprint(ADMIN_BLUEPRINT, url_prefix='/oapi')

try:
from flask_cors import CORS

CORS(app)
except ImportError: # CORS needs to be handled by upstream server
pass
Expand Down
137 changes: 137 additions & 0 deletions wis2box_api/asyncapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
###############################################################################
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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 os
import logging
from typing import Any, Tuple, Union

from pygeoapi.api import API, APIRequest, F_HTML, pre_process
from pygeoapi import l10n
from pygeoapi.util import to_json, render_j2_template

from wis2box_api import __version__


LOGGER = logging.getLogger(__name__)


class AsyncAPI(API):
"""AsyncAPI object"""

PYGEOAPI_CONFIG = os.environ.get("PYGEOAPI_CONFIG")
PYGEOAPI_OPENAPI = os.environ.get("PYGEOAPI_OPENAPI")

def __init__(self, config):
"""
constructor
:param config: configuration dict
:returns: `wis2box_api.AsyncAPI` instance
"""

super().__init__(config)

@pre_process
def get_asyncapi(self, request: Union[APIRequest, Any]) -> Tuple[dict, int, str]: # noqa
"""
Provide AsyncAPI document
:param request: request object
:returns: tuple of headers, status code, content
"""

if not request.is_valid():
return self.get_format_exception(request)

headers = request.get_response_headers()

content = to_json(generate_asyncapi(self.config, request.locale),
self.pretty_print)

return headers, 200, content

if request.format == F_HTML:
content = render_j2_template(
self.config, 'admin/index.html', self.config, request.locale
)
else:
content = to_json(generate_asyncapi(), self.pretty_print)

return headers, 200, content


def generate_asyncapi(config: dict, locale: str) -> dict:
"""
Generate an AsyncAPI document
:param config: `dict` of pygeoapi configuration
:param language: A locale string (e.g. "en-US" or "en") or Babel Locale.
:returns: `dict` of AsyncAPI document
"""

LOGGER.debug('Generating AsyncAPI document')

title = l10n.translate(config['metadata']['identification']['title'], locale) # noqa
description = l10n.translate(config['metadata']['identification']['description'], locale) # noqa
tags = l10n.translate(config['metadata']['identification']['keywords'], locale) # noqa

a = {
'asyncapi': '2.4.0',
'id': 'https://github.com/wmo-im/wis2box',
'info': {
'version': __version__,
'title': title,
'description': description,
'license': {
'name': config['metadata']['license']['name'],
'url': config['metadata']['license']['url']
},
'contact': {
'name': config['metadata']['contact']['name'],
'url': config['metadata']['contact']['url'],
'email': config['metadata']['contact']['email']
}
},
'servers': {
'production': {
'url': os.environ.get('WIS2BOX_MQTT_URL'),
'protocol': 'mqtt',
'protocolVersion': '5.0',
'description': description
}
},
'channels': {
'origin/a/wis2': {
'subscribe': {
'operationId': 'ClientSubscribed',
'message': {
'$ref': 'https://raw.githubusercontent.com/wmo-im/wis2-notification-message/main/WIS2_Message_Format_Schema.yaml' # noqa
}
}
}
},
'tags': [{'name': tag} for tag in tags]
}

return a
58 changes: 58 additions & 0 deletions wis2box_api/flask_asyncapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
###############################################################################
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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 os
import logging

from flask import Blueprint, request

from pygeoapi.flask_app import get_response
from pygeoapi.util import yaml_load

from wis2box_api.asyncapi import AsyncAPI

LOGGER = logging.getLogger(__name__)

CONFIG = None

if 'PYGEOAPI_CONFIG' not in os.environ:
raise RuntimeError('PYGEOAPI_CONFIG environment variable not set')

with open(os.environ.get('PYGEOAPI_CONFIG'), encoding='utf8') as fh:
CONFIG = yaml_load(fh)

asyncapi_ = AsyncAPI(CONFIG)

ASYNCAPI_BLUEPRINT = Blueprint(
'asyncapi',
__name__
)


@ASYNCAPI_BLUEPRINT.route('/')
def home():
"""
AsyncAPI endpoint
:returns: HTTP response
"""

return get_response(asyncapi_.get_asyncapi(request))

0 comments on commit 4fdcae9

Please sign in to comment.