picasso/picasso/api/controllers/runnable.py

158 lines
5.0 KiB
Python

# All Rights Reserved.
#
# 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 aiohttp import web
from aioservice.http import controller
from aioservice.http import requests
from ...common import config
from ...models import app as app_model
class RunnableMixin(object):
async def run(self, request, **kwargs):
c = config.Config.config_instance()
fnclient = c.functions_client
app = request.match_info.get('app')
path = "/{}".format(request.match_info.get('route'))
try:
data = await request.json()
except Exception:
# in may appear that no data supplied with POST to function
data = {}
try:
fn_app = await fnclient.apps.show(
app, loop=c.event_loop)
route = await fn_app.routes.show(
path, loop=c.event_loop)
result = await fn_app.routes.execute(
path, loop=c.event_loop, **data)
except Exception as ex:
return web.json_response(data={
"error": {
"message": getattr(ex, "reason", str(ex)),
}
}, status=getattr(ex, "status", 500))
def process_result(res):
if route.type == "async":
_data = {
"task_id": res["call_id"],
"message": ("App {} async route {} "
"execution started".format(app, path))
}
else:
_data = {
"result": res,
"message": ("App {} sync route {} "
"execution finished".format(app, path))
}
return _data
return web.json_response(status=200, data=process_result(result))
class PublicRunnableV1Controller(controller.ServiceController,
RunnableMixin):
controller_name = "public_runnable"
# IronFunction uses `r` as runnable instead API version
version = "r"
@requests.api_action(
method='POST', route='{app}/{route}')
async def run(self, request, **kwargs):
"""
---
description: Running public app route
tags:
- Runnable
produces:
- application/json
responses:
"200":
description: successful operation. Return "runnable" JSON
"404":
description: App does not exist
"403":
description: Unable to execute private route
"""
app = request.match_info.get('app')
path = request.match_info.get('route')
routes = await app_model.Routes.find_by(app_name=app, path=path)
if not routes:
return web.json_response(data={
"error": {
"message": "Route {0} not found".format(app),
}
}, status=404)
route = routes.pop()
if not route.public:
return web.json_response(data={
"error": {
"message": "Unable to execute private "
"route {0}".format(path)
}
}, status=403)
return await super(PublicRunnableV1Controller,
self).run(request, **kwargs)
class RunnableV1Controller(controller.ServiceController,
RunnableMixin):
controller_name = "runnable"
# IronFunction uses `r` as runnable instead API version
version = "v1"
@requests.api_action(
method='POST', route='r/{project_id}/{app}/{route}')
async def run(self, request, **kwargs):
"""
---
description: Running private app route
tags:
- Runnable
produces:
- application/json
responses:
"401":
description: Not authorized.
"200":
description: successful operation. Return "runnable" JSON
"404":
description: App not found
"404":
description: App route not found
"""
app = request.match_info.get('app')
project_id = request.match_info.get('project_id')
if not (await app_model.Apps.exists(app, project_id)):
return web.json_response(data={
"error": {
"message": "App {0} not found".format(app),
}
}, status=404)
return await super(RunnableV1Controller,
self).run(request, **kwargs)