async def read_multipart(reader: MultipartReader) -> Tuple[dict, bytearray]: part1 = await reader.next() if part1 is None or part1.headers[hdrs.CONTENT_TYPE] != 'application/json': raise web.HTTPNotAcceptable(reason='expected metadata') metadata = await part1.json() part2 = await reader.next() if part2 is None or part2.headers[hdrs.CONTENT_TYPE] != 'image/jpeg': raise web.HTTPNotAcceptable(reason='expected image data') jpeg = await part2.read(decode=False) return (metadata, jpeg)
async def handler(request): body = await request.json() state_str = body.get('state') state = request.app.openkart.State.__members__.get(state_str) if state is None: raise web.HTTPNotAcceptable(text=f'unknown state {state_str}') try: await request.app.openkart.set_state(state) except AttributeError: raise web.HTTPNotAcceptable(text='server not configured for this') return await get_state_handler(request)
async def game(request): if request.method == 'GET': async with request.app['db'].acquire() as conn: # GET all games and display gamename and status cursor = await conn.execute(db.game.select()) records = await cursor.fetchall() games = str([(i[0], i[1]) for i in records]) print('records ', records) return web.Response(text='Games are: ' + games) elif request.method == 'POST': try: # inserts a new game into the game table # throws IntegrityError if game already exists with this name data = await request.post() game_name = data['name'] async with request.app['db'].acquire() as conn: await conn.execute(db.game.insert().values(name=game_name, status='NEW')) except (KeyError, TypeError, ValueError) as e: raise web.HTTPBadRequest( text='You have not specified a game name') from e except (IntegrityError): raise web.HTTPNotAcceptable(text="a game called " + game_name + " already exists.") return web.Response(text='New Game: ' + game_name + ' has been created') return web.HTTPBadRequest(text='bad request type')
async def purge_all(request): try: json_body = await request.json() except JSONDecodeError: raise web.HTTPNotAcceptable(reason="Invalid json") return await base.purge_all(json_body)
async def post_index(request): if request.headers['Content-Type'] != 'application/json': raise web.HTTPNotAcceptable() data = await parser.parse(SizeChart, request) return web.json_response({})
async def put_chart(request): if request.headers['Content-Type'] != 'application/json': raise web.HTTPNotAcceptable() chart_id = request.match_info['id'] data = await parser.parse(SizeChart, request) return web.json_response({})
def best_content_type(request: web.Request) -> str: # language=rst """The best matching Content-Type. Todo: Generalize. (Now, we only negotiate hal+json and friends.) Raises: aiohttp.web.HTTPNotAcceptable: if none of the available content types are acceptable by the client. See :ref:`aiohttp web exceptions <aiohttp-web-exceptions>`. """ if 'ACCEPT' not in request.headers: return "application/hal+json; charset=UTF-8" accept = ','.join(request.headers.getall('ACCEPT', ['*/*'])) mime_types = [part.split(';', 2)[0].strip() for part in accept.split(',')] if not _HAL_JSON_COMPATIBLE.isdisjoint(mime_types): return "application/hal+json; charset=UTF-8" elif "application/json" in mime_types: return "application/json; charset=UTF-8" else: body = ",".join(_AVAILABLE_CONTENT_TYPES).encode('ascii') raise web.HTTPNotAcceptable( body=body, content_type='text/plain; charset="US-ASCII"')
def parse_accept(self, request, content_types): """Parse the incoming Accept: headers for content types matching the available handlers Currently this method does not care about the quality Accept parameter (e.g. 'text/plain;q=0.5') :param request: the incoming HTTP request :type request: aiohttp.Request :param content_types: list of content types that can be produced :type content_types: list(string) :returns: The first matching item, or None if the incoming request has no Accept headers. :rtype: string :raises aiohttp.web.HTTPNotAcceptable: if incoming Accept headers can not be matched against any of the available produced types. """ client_acceptables = [ accept.split(';')[0] for accept in ','.join( request.headers.getall('ACCEPT', [])).split(',') ] self.log.info('acceptables: %r', client_acceptables) if len(client_acceptables) == 0 or (len(client_acceptables) == 1 and len(client_acceptables[0]) == 0): return None for accept in client_acceptables: for content_type in content_types: if fnmatch.fnmatch(content_type, accept): return content_type raise web.HTTPNotAcceptable()
def abort(code): if code == 400: return web.HTTPBadRequest() elif code == 401: return web.HTTPUnauthorized() elif code == 402: return web.HTTPPaymentRequired() elif code == 403: return web.HTTPForbidden() elif code == 404: return web.HTTPNotFound() elif code == 405: return web.HTTPMethodNotAllowed() elif code == 406: return web.HTTPNotAcceptable() elif code == 407: return web.HTTPProxyAuthenticationRequired() elif code == 408: return web.HTTPRequestTimeout() elif code == 409: return web.HTTPConflict() elif code == 410: return web.HTTPGone() elif code == 411: return web.HTTPLengthRequired() elif code == 412: return web.HTTPPreconditionFailed() elif code == 413: return web.HTTPRequestEntityTooLarge() elif code == 414: return web.HTTPRequestURITooLong() elif code == 415: return web.HTTPUnsupportedMediaType() elif code == 416: return web.HTTPRequestRangeNotSatisfiable() elif code == 417: return web.HTTPExpectationFailed() elif code == 421: return web.HTTPMisdirectedRequest() elif code == 422: return web.HTTPUnprocessableEntity() elif code == 424: return web.HTTPFailedDependency() elif code == 426: return web.HTTPUpgradeRequired() elif code == 428: return web.HTTPPreconditionRequired() elif code == 429: return web.HTTPTooManyRequests() elif code == 431: return web.HTTPRequestHeaderFieldsTooLarge() elif code == 451: return web.HTTPUnavailableForLegalReasons() else: return web.HTTPBadRequest()
async def wrapper(request): # First, check that client requests supported output format. is_text_output = False accepts = ",".join(request.headers.getall("Accept", [])) # Text is rendered only if explicitly specified. if "text/plain" in accepts: is_text_output = True elif "*/*" not in accepts and "application/json" not in accepts: # Client is requesting an unknown format. raise web.HTTPNotAcceptable() # Execute the decorated view. view_result = await func(request) # Render the response. results = [view_result] if isinstance(view_result, dict) else view_result all_success = all(c["success"] for c in results) status_code = 200 if all_success else 503 if is_text_output: # Multiple checks can be rendered as text to be easier # to read (eg. in Pingdom "Root cause" UI). max_project_length = max([len(c["project"]) for c in results]) max_name_length = max([len(c["name"]) for c in results]) text = "\n".join([(check["project"].ljust(max_project_length + 2) + check["name"].ljust(max_name_length + 2) + repr(check["success"])) for check in results]) # Let's add some details about each failing check at the bottom. fields = ( "url", "description", "documentation", "parameters", "data", "troubleshooting", ) for check in [c for c in results if not c["success"]]: text += "\n" * 2 + "\n{project} {name}\n".format(**check) check = { **check, "parameters": repr(check["parameters"]), "data": json.dumps(check["data"], indent=2), } text += "\n".join( chain(*[( " " + field.capitalize() + ":", textwrap.indent(check[field], " "), ) for field in fields])) return web.Response(text=text, status=status_code) # Default rendering is JSON. return web.json_response(view_result, status=status_code)
def render_mako(request, data): handler = request.match_info.handler template_name = handler.__dict__.get('template', None) if not template_name: raise web.HTTPNotAcceptable(text='text/html not supported.') if not isinstance(data, Mapping): raise web.HTTPInternalServerError( text="context should be mapping, not {}".format(type(data))) template = request.app['mako_lookup'].get_template(template_name) text = template.render_unicode(**data) return web.Response(text=text, content_type=request['selected_media_type'])
def _validate_and_extract_from(ws_msg): try: json_data = json.loads(ws_msg) except json.JSONDecodeError: raise web.HTTPNotAcceptable(reason="Invalid json") v = Validator(schemas.WEB_SOCKET_MSG) if not v.validate(json_data): raise web.HTTPBadRequest(reason=v.errors) return json_data['command'], json_data['body']
def _best_content_type(request: web.Request, available_content_types: T.List[str]) -> str: # language=rst """The best matching content type. Returns: The best content type to use for the HTTP response, given a certain ``request`` and a list of ``available_content_types``. Parameters: request (aiohttp.web.Request): the current request available_content_types: an ordered list of available content types, ordered by quality, best quality first. Raises: web.HTTPNotAcceptable: if none of the available content types are acceptable by the client. See :ref:`aiohttp web exceptions <aiohttp-web-exceptions>`. Example:: AVAILABLE = [ 'foo/bar', 'foo/baz; charset="utf-8"' ] def handler(request): bct = best_content_type(request, AVAILABLE) """ if hdrs.ACCEPT not in request.headers: return available_content_types[0] accept = ','.join(request.headers.getall(hdrs.ACCEPT)) acceptable_content_types = dict() for acceptable in accept.split(','): try: main, sub = acceptable.split(';', 2)[0].strip().split('/', 2) except ValueError: raise web.HTTPBadRequest(text="Malformed Accept: header") from None if main not in acceptable_content_types: acceptable_content_types[main] = set() acceptable_content_types[main].add(sub) # Try to find a matching 'main/sub' or 'main/*': for available in available_content_types: main, sub = available.split(';', 2)[0].strip().split('/', 2) if main in acceptable_content_types: subs = acceptable_content_types[main] if sub in subs or '*' in subs: return available if '*' in acceptable_content_types and '*' in acceptable_content_types['*']: return available_content_types[0] # Darn, none of our content types are acceptable to the client: body = ",".join(available_content_types).encode('ascii') raise web.HTTPNotAcceptable(body=body, content_type='text/plain; charset="US-ASCII"')
async def handle_request(self, request: web.Request) -> web.Response: request_time = int(time() * 1000) try: # Check if the Content-Type and Accept headers both are presented # and contain 'application/json' if request.headers["Content-Type"] != "application/json": raise web.HTTPUnsupportedMediaType( reason="Invalid Content-Type") if request.headers["Accept"] != "application/json": raise web.HTTPNotAcceptable(reason="Invalid Accept header") except KeyError as exp: reason = "{} header is required".format(exc_message(exp)) raise web.HTTPNotAcceptable(reason=reason) try: request_data = await request.json() except json.JSONDecodeError: exp = JsonRpcError(Errors.PARSE_ERROR) self.log_request(request, request_time, "invalid", exp) return exp.http_response() try: if isinstance(request_data, list): response_data = await self.process_batch_rpc(request_data) self.log_request(request, request_time) return web.json_response(response_data) response_data = await self.process_single_rpc(request_data) self.log_request(request, request_time, request_data["method"]) return web.json_response(response_data) except JsonRpcError as exp: exp.set_context(request_data) self.log_request(request, request_time, request_data.get("method", "unknown/invalid"), exp) return exp.http_response()
async def middleware(request: web.Request) -> web.Response: """ Middleware handler. """ if request.method == "GET": request.data = request.query_string else: content_type = request.content_type if content_type == "application/json": try: request.data = await request.json() except JSONDecodeError: logger.error("Invalid JSON received") raise web.HTTPBadRequest else: logger.error("Unsupported content type '%s'.", request.content_type) return web.HTTPNotAcceptable() for accept in request.headers.getall('ACCEPT', []): if accept == "application/json": data_out = await handler(request) return web.json_response(data_out) logger.error("Unsupported ACCEPT header '%s'.", accept) return web.HTTPNotAcceptable()
async def delete_user(request): """ Delete a user from users table :Example: curl -H "authorization: <token>" -X DELETE http://localhost:8081/fledge/admin/{user_id}/delete """ if request.is_auth_optional: _logger.warning(FORBIDDEN_MSG) raise web.HTTPForbidden # TODO: we should not prevent this, when we have at-least 1 admin (super) user try: user_id = int(request.match_info.get('user_id')) except ValueError as ex: _logger.warning(str(ex)) raise web.HTTPBadRequest(reason=str(ex)) if user_id == 1: msg = "Super admin user can not be deleted" _logger.warning(msg) raise web.HTTPNotAcceptable(reason=msg) # Requester should not be able to delete her/himself if user_id == request.user["id"]: msg = "You can not delete your own account" _logger.warning(msg) raise web.HTTPBadRequest(reason=msg) try: result = await User.Objects.delete(user_id) if not result['rows_affected']: raise User.DoesNotExist except ValueError as ex: _logger.warning(str(ex)) raise web.HTTPBadRequest(reason=str(ex)) except User.DoesNotExist: msg = "User with id:<{}> does not exist".format(int(user_id)) _logger.warning(msg) raise web.HTTPNotFound(reason=msg) except Exception as exc: _logger.exception(str(exc)) raise web.HTTPInternalServerError(reason=str(exc)) _logger.info("User with id:<{}> has been deleted successfully.".format( int(user_id))) return web.json_response({'message': "User has been deleted successfully"})
async def delete(self, request): """ Удаление пользователя :param request: :return: """ db = request.app['db'] auth = request.auth if request.auth.get('group') != 1: return web.HTTPUnauthorized() if not auth.get('client'): return web.HTTPUnauthorized() id = request.match_info.get('id', 0) user = await one( db, User.get_by([User.id == id, User.client_id == auth.get('client')])) del_user_result = bool(await delete( db, User.remove([User.id == id, User.client_id == auth.get('client')]))) del_params_result = bool(await delete( db, Param.remove([Param.user_id == user.get('id')]))) if not del_user_result: raise web.HTTPNotAcceptable( reason='Can`t delete user. Try again later ', body='Can`t delete user. Try again later') # статистика stat = { 'user_id': auth.get('id'), 'code': '17', 'msg': 'User deleted [' + str(id) + ']' } await insert(db, Statistic.add(stat)) # разлогиневаем удаленного cache = request.app.get('cache') await cache.kill_cache('uid_token_' + str(id)) return web.json_response(user, dumps=self.__encode_helper, headers={'X-Total-Count': '1'})
def IDputClasslist(self, data, request): """Accept classlist upload. Only "manager" can perform this action. The classlist should be provided as a ordered list of (str, str) pairs where each pair is (student ID, student name). Side effects on the server test spec file: * If numberToName and/or numberToProduce are -1, values are set based on this classlist (spec is permanently altered)/ * If numberToName < 0 but numberToProduce is too small for the result, respond with HTTPNotAcceptable. Returns: aiohttp.web_response.Response: Success or failure. Can be: 200: success 400: authentication problem. HTTPBadRequest: not manager, or malformed request. HTTPConflict: we already have a classlist. TODO: would be nice to be able to "try again". HTTPNotAcceptable: classlist too short (see above). """ if not data["user"] == "manager": raise web.HTTPBadRequest(reason="Not manager") if os.path.isfile(Path(specdir) / "classlist.csv"): raise web.HTTPConflict(reason="we already have a classlist") classlist = data["classlist"] # TODO should we make copy until sure it passes verification? spec = self.server.testSpec if spec.numberToName < 0 or spec.numberToProduce < 0: if spec.number_to_name < 0: spec.set_number_papers_to_name(len(classlist)) if spec.number_to_produce < 0: spec.set_number_papers_add_spares(len(classlist)) try: spec.verifySpec(verbose="log") except ValueError as e: raise web.HTTPNotAcceptable(reason=str(e)) spec.saveVerifiedSpec() with open(Path(specdir) / "classlist.csv", "w") as csvfile: writer = csv.writer(csvfile) writer.writerow(["id", "studentName"]) writer.writerows(classlist) return web.Response()
async def __call__(self, request): authorization = request.headers.get('Authorization') # TODO: can authorization be '' or is None test? if not authorization: authorization = request.query.get('access_token') await check_authorization(request, self.acl, authorization) try: accepted = next(x[0] for x in request.headers.getall('ACCEPT', []) if x[0].startswith(self.HEADER_PREFIX)) accepted_version = int(accepted[len(self.HEADER_PREFIX):]) except (StopIteration, ValueError): accepted_version = self.MIN_API_VERSION try: func = next(func for version, func in self.funcs if version <= accepted_version) except StopIteration: raise web.HTTPNotAcceptable() return await func(request)
async def Event(request: web.Request) -> EventType: user_agent = request.headers.get('User-Agent', None) if not user_agent.startswith('GitHub-Hookshot/'): raise web.HTTPNotAcceptable(reason='User agent looks incorrect') content_type = request.headers.get('Content-Type', None) if not content_type == 'application/json': raise web.HTTPUnsupportedMediaType(reason='Not a JSON payload') payload = await request.json() event_type = request.headers.get('X-GitHub-Event', None) key = 'issue' if event_type == 'issues' else event_type return EventType( app=request.app, key=key, type=event_type, guid=request.headers.get('X-GitHub-Delivery', None), signature=request.headers.get('X-Hub-Signature', None), user_agent=user_agent, content_type=content_type, payload=payload, action=payload.get('action', None), )
def error(self, request: web.Request = None, response: dict = {}, exception: Exception = None, state: int = 400, headers: dict = {}, **kwargs) -> web.Response: # TODO: process the exception object response_obj = {"status": "Failed"} if not request: request = self.request if exception: response_obj["reason"] = str(exception) args = {**kwargs} if isinstance(response, dict): response_obj = {**response_obj, **response} args["content_type"] = "application/json" args["text"] = json.dumps(response_obj) else: args["body"] = response # defining the error if state == 400: # bad request obj = web.HTTPBadRequest(**args) elif state == 401: # unauthorized obj = web.HTTPUnauthorized(**args) elif state == 403: # forbidden obj = web.HTTPForbidden(**args) elif state == 404: # not found obj = web.HTTPNotFound(**args) elif state == 406: obj = web.HTTPNotAcceptable(**args) elif state == 412: obj = web.HTTPPreconditionFailed(**args) elif state == 428: obj = web.HTTPPreconditionRequired(**args) else: obj = web.HTTPBadRequest(**args) for header, value in headers.items(): obj.headers[header] = value return obj
async def post(self) -> Union[web.json_response, web.HTTPException]: """ Take user credentials and check if user exist. If exist - return his profile in JSON. Else -> HTTPException Returns: User profile or Exception """ request_data = await self.request.json() result = await self.find_user(request_data) if not result: raise web.HTTPNotFound(text="User not found") profile = Profile(**clean_data(result)) if profile.check_password(request_data['password']): await profile.fetch_contacts(self.request.app['db']) return web.json_response(profile.to_dict(), status=200) raise web.HTTPNotAcceptable(text="Wrong password")
def serialize(self, data): raise web.HTTPNotAcceptable( body='') # TODO respond with all acceptable content-types
async def is_ready(self, request): if self.bot.is_ready(): raise web.HTTPOk() raise web.HTTPNotAcceptable()
async def is_connected(self, request): if self.connected: raise web.HTTPOk() raise web.HTTPNotAcceptable()
async def liveness_probe(self, request): if self.bot.is_ready(): raise web.HTTPOk() raise web.HTTPNotAcceptable()
def handler_not_acceptable(self): raise web.HTTPNotAcceptable(text=json.dumps(NOT_ACCEPTABLE_MSG), content_type="application/json")
def raiseIfNotAllowed(self): if not self.shouldHandle: raise web.HTTPNotAcceptable(body='')
async def reset(request): """ reset user (only role and password) :Example: curl -H "authorization: <token>" -X PUT -d '{"role_id": "1"}' http://localhost:8081/fledge/admin/{user_id}/reset curl -H "authorization: <token>" -X PUT -d '{"password": "******"}' http://localhost:8081/fledge/admin/{user_id}/reset curl -H "authorization: <token>" -X PUT -d '{"role_id": 1, "password": "******"}' http://localhost:8081/fledge/admin/{user_id}/reset """ if request.is_auth_optional: _logger.warning(FORBIDDEN_MSG) raise web.HTTPForbidden user_id = request.match_info.get('user_id') if int(user_id) == 1: msg = "Restricted for Super Admin user" _logger.warning(msg) raise web.HTTPNotAcceptable(reason=msg) data = await request.json() password = data.get('password') role_id = data.get('role_id') if not role_id and not password: msg = "Nothing to update the user" _logger.warning(msg) raise web.HTTPBadRequest(reason=msg) if role_id and not (await is_valid_role(role_id)): msg = "Invalid or bad role id" _logger.warning(msg) return web.HTTPBadRequest(reason=msg) if password and not isinstance(password, str): _logger.warning(PASSWORD_ERROR_MSG) raise web.HTTPBadRequest(reason=PASSWORD_ERROR_MSG) if password and not re.match(PASSWORD_REGEX_PATTERN, password): _logger.warning(PASSWORD_ERROR_MSG) raise web.HTTPBadRequest(reason=PASSWORD_ERROR_MSG) user_data = {} if 'role_id' in data: user_data.update({'role_id': data['role_id']}) if 'password' in data: user_data.update({'password': data['password']}) try: await User.Objects.update(user_id, user_data) except ValueError as ex: _logger.warning(str(ex)) raise web.HTTPBadRequest(reason=str(ex)) except User.DoesNotExist: msg = "User with id:<{}> does not exist".format(int(user_id)) _logger.warning(msg) raise web.HTTPNotFound(reason=msg) except User.PasswordAlreadyUsed: msg = "The new password should be different from previous 3 used" _logger.warning(msg) raise web.HTTPBadRequest(reason=msg) except Exception as exc: _logger.exception(str(exc)) raise web.HTTPInternalServerError(reason=str(exc)) _logger.info("User with id:<{}> has been updated successfully".format( int(user_id))) return web.json_response({ 'message': 'User with id:<{}> has been updated successfully'.format(user_id) })
async def add_service(request): """ Create a new service to run a specific plugin :Example: curl -X POST http://localhost:8081/foglamp/service -d '{"name": "DHT 11", "plugin": "dht11", "type": "south", "enabled": true}' curl -sX POST http://localhost:8081/foglamp/service -d '{"name": "Sine", "plugin": "sinusoid", "type": "south", "enabled": true, "config": {"dataPointsPerSec": {"value": "10"}}}' | jq curl -X POST http://localhost:8081/foglamp/service -d '{"name": "NotificationServer", "type": "notification", "enabled": true}' | jq """ try: data = await request.json() if not isinstance(data, dict): raise ValueError('Data payload must be a valid JSON') name = data.get('name', None) plugin = data.get('plugin', None) service_type = data.get('type', None) enabled = data.get('enabled', None) config = data.get('config', None) if name is None: raise web.HTTPBadRequest( reason='Missing name property in payload.') if utils.check_reserved(name) is False: raise web.HTTPBadRequest( reason='Invalid name property in payload.') if utils.check_foglamp_reserved(name) is False: raise web.HTTPBadRequest( reason= "'{}' is reserved for FogLAMP and can not be used as service name!" .format(name)) if service_type is None: raise web.HTTPBadRequest( reason='Missing type property in payload.') service_type = str(service_type).lower() if service_type == 'north': raise web.HTTPNotAcceptable( reason='north type is not supported for the time being.') if service_type not in ['south', 'notification']: raise web.HTTPBadRequest( reason='Only south and notification type are supported.') if plugin is None and service_type == 'south': raise web.HTTPBadRequest( reason='Missing plugin property for type south in payload.') if plugin and utils.check_reserved(plugin) is False: raise web.HTTPBadRequest( reason='Invalid plugin property in payload.') if enabled is not None: if enabled not in ['true', 'false', True, False]: raise web.HTTPBadRequest( reason='Only "true", "false", true, false' ' are allowed for value of enabled.') is_enabled = True if ( (type(enabled) is str and enabled.lower() in ['true']) or ((type(enabled) is bool and enabled is True))) else False # Check if a valid plugin has been provided plugin_module_path, plugin_config, process_name, script = "", {}, "", "" if service_type == 'south': # "plugin_module_path" is fixed by design. It is MANDATORY to keep the plugin in the exactly similar named # folder, within the plugin_module_path. # if multiple plugin with same name are found, then python plugin import will be tried first plugin_module_path = "foglamp.plugins.south" try: plugin_info = load_python_plugin(plugin_module_path, plugin, service_type) plugin_config = plugin_info['config'] if not plugin_config: _logger.exception("Plugin %s import problem from path %s", plugin, plugin_module_path) raise web.HTTPNotFound( reason='Plugin "{}" import problem from path "{}".'. format(plugin, plugin_module_path)) process_name = 'south_c' script = '["services/south_c"]' except ImportError as ex: # Checking for C-type plugins plugin_config = load_c_plugin(plugin, service_type) if not plugin_config: _logger.exception( "Plugin %s import problem from path %s. %s", plugin, plugin_module_path, str(ex)) raise web.HTTPNotFound( reason='Plugin "{}" import problem from path "{}".'. format(plugin, plugin_module_path)) process_name = 'south_c' script = '["services/south_c"]' except TypeError as ex: _logger.exception(str(ex)) raise web.HTTPBadRequest(reason=str(ex)) except Exception as ex: _logger.exception("Failed to fetch plugin configuration. %s", str(ex)) raise web.HTTPInternalServerError( reason='Failed to fetch plugin configuration') elif service_type == 'notification': process_name = 'notification_c' script = '["services/notification_c"]' storage = connect.get_storage_async() config_mgr = ConfigurationManager(storage) # Check whether category name already exists category_info = await config_mgr.get_category_all_items( category_name=name) if category_info is not None: raise web.HTTPBadRequest( reason="The '{}' category already exists".format(name)) # Check that the schedule name is not already registered count = await check_schedules(storage, name) if count != 0: raise web.HTTPBadRequest( reason='A service with this name already exists.') # Check that the process name is not already registered count = await check_scheduled_processes(storage, process_name) if count == 0: # Now first create the scheduled process entry for the new service payload = PayloadBuilder().INSERT(name=process_name, script=script).payload() try: res = await storage.insert_into_tbl("scheduled_processes", payload) except StorageServerError as ex: _logger.exception("Failed to create scheduled process. %s", ex.error) raise web.HTTPInternalServerError( reason='Failed to create service.') except Exception as ex: _logger.exception("Failed to create scheduled process. %s", str(ex)) raise web.HTTPInternalServerError( reason='Failed to create service.') # check that notification service is not already registered, right now notification service LIMIT to 1 if service_type == 'notification': res = await check_notification_schedule(storage) for ps in res['rows']: if 'notification_c' in ps['process_name']: raise web.HTTPBadRequest( reason='A Notification service schedule already exists.' ) elif service_type == 'south': try: # Create a configuration category from the configuration defined in the plugin category_desc = plugin_config['plugin']['description'] await config_mgr.create_category( category_name=name, category_description=category_desc, category_value=plugin_config, keep_original_items=True) # Create the parent category for all South services await config_mgr.create_category("South", {}, "South microservices", True) await config_mgr.create_child_category("South", [name]) # If config is in POST data, then update the value for each config item if config is not None: if not isinstance(config, dict): raise ValueError('Config must be a JSON object') for k, v in config.items(): await config_mgr.set_category_item_value_entry( name, k, v['value']) except Exception as ex: await config_mgr.delete_category_and_children_recursively(name) _logger.exception("Failed to create plugin configuration. %s", str(ex)) raise web.HTTPInternalServerError( reason='Failed to create plugin configuration.') # If all successful then lastly add a schedule to run the new service at startup try: schedule = StartUpSchedule() schedule.name = name schedule.process_name = process_name schedule.repeat = datetime.timedelta(0) schedule.exclusive = True # if "enabled" is supplied, it gets activated in save_schedule() via is_enabled flag schedule.enabled = False # Save schedule await server.Server.scheduler.save_schedule(schedule, is_enabled) schedule = await server.Server.scheduler.get_schedule_by_name(name) except StorageServerError as ex: await config_mgr.delete_category_and_children_recursively(name) _logger.exception("Failed to create schedule. %s", ex.error) raise web.HTTPInternalServerError( reason='Failed to create service.') except Exception as ex: await config_mgr.delete_category_and_children_recursively(name) _logger.exception("Failed to create service. %s", str(ex)) raise web.HTTPInternalServerError( reason='Failed to create service.') except ValueError as e: raise web.HTTPBadRequest(reason=str(e)) else: return web.json_response({ 'name': name, 'id': str(schedule.schedule_id) })