def test_dotted_name(): assert utils.get_dotted_name( FooBar()) == 'guillotina.tests.test_utils.FooBar' assert utils.get_dotted_name( FooBar) == 'guillotina.tests.test_utils.FooBar' assert utils.get_module_dotted_name( FooBar()) == 'guillotina.tests.test_utils' assert utils.get_module_dotted_name( FooBar) == 'guillotina.tests.test_utils' assert utils.get_dotted_name( IResource) == 'guillotina.interfaces.content.IResource'
def test_dotted_name(): assert utils.get_dotted_name( FooBar()) == 'guillotina.tests.test_utils.FooBar' assert utils.get_dotted_name( FooBar) == 'guillotina.tests.test_utils.FooBar' assert utils.get_module_dotted_name( FooBar()) == 'guillotina.tests.test_utils' assert utils.get_module_dotted_name( FooBar) == 'guillotina.tests.test_utils' assert utils.get_dotted_name( IResource) == 'guillotina.interfaces.content.IResource'
async def get_all_subscribers(context, request): subscribers = {} sm = component.get_global_components() for registration in sm.registeredHandlers(): if len(registration.required) != 2: continue handler = get_dotted_name(registration.handler) event = get_dotted_name(registration.required[1]) resource = get_dotted_name(registration.required[0]) if resource not in subscribers: subscribers[resource] = {} if event not in subscribers[resource]: subscribers[resource][event] = [] subscribers[resource][event].append(handler) return subscribers
async def get_all_subscribers(context, request): subscribers = {} sm = component.get_global_components() for registration in sm.registeredHandlers(): if len(registration.required) != 2: continue handler = get_dotted_name(registration.handler) event = get_dotted_name(registration.required[1]) resource = get_dotted_name(registration.required[0]) if resource not in subscribers: subscribers[resource] = {} if event not in subscribers[resource]: subscribers[resource][event] = [] subscribers[resource][event].append(handler) return subscribers
def includeme(root): configure.scan("guillotina_elasticsearch.utility") configure.scan("guillotina_elasticsearch.manager") configure.scan("guillotina_elasticsearch.parser") # add store true to guillotina indexes for name, utility in get_utilities_for(IResourceFactory): if not get_dotted_name(utility._callable).startswith("guillotina."): continue for field_name, catalog_info in get_index_fields(name).items(): if field_name in ( "id", "path", "uuid", "type_name", "tid", "creators", "contributors", "access_roles", "access_users", "parent_uuid", "title", "creation_date", "modification_date", "tags", ): catalog_info["store"] = True
def get_service_def(self, path, method=None, type_name=None): base_path, subpath, path_type_name = self.get_service_type_name(path) if type_name is not None: services = self.get_type_services(type_name) else: services = self.get_type_services(path_type_name) try: if '@' in path: return services['endpoints']['@' + subpath][method.upper()] else: return services[method] except (KeyError, TypeError): if type_name is not None: raise # this shouldn't happen, so we lookup other interfaces that this inherits # from to find service we're looking for iface = resolve_dotted_name(path_type_name) service = None for other_iface in iface.__bases__: try: service = self.get_service_def( path, method, get_dotted_name(other_iface)) except (KeyError, TypeError): pass if service is not None: return service
def run_command(self): settings = get_settings(self.arguments.configuration) app = self.make_app(settings) if self.arguments.line_profiler: if not HAS_LINE_PROFILER: sys.stderr.write( 'You must first install line_profiler for the --line-profiler option to work.\n' 'Use `pip install line_profiler` to install line_profiler.\n' ) return 1 self.line_profiler = line_profiler.LineProfiler() for func in profile.get_profilable_functions(): if fnmatch(get_dotted_name(func), self.arguments.line_profiler_matcher or '*'): self.line_profiler.add_function(func) self.line_profiler.enable_by_count() run_func = self.__run if self.arguments.monitor: if not HAS_AIOMONITOR: sys.stderr.write( 'You must install aiomonitor for the ' '--monitor option to work.\n' 'Use `pip install aiomonitor` to install aiomonitor.\n') return 1 run_func = self.__run_with_monitor if self.arguments.profile: self.profiler = cProfile.Profile() self.profiler.runcall(run_func, app, settings) else: run_func(app, settings)
def convert_interfaces_to_schema(interfaces): properties = {} for iface in interfaces: properties[get_dotted_name(iface)] = { "type": "object", "properties": convert_interface_to_schema(iface) } return properties
async def test_job_function_name(): data = { "func": get_dotted_name(_run_object_task), "args": ["my.func.foobar"] } job = Job(None, data, None, None) assert job.function_name == "run_object_task/my.func.foobar" data = { "func": get_dotted_name(_yield_object_task), "args": ["my.func.foobar"] } job = Job(None, data, None, None) assert job.function_name == "yield_object_task/my.func.foobar" data = {"func": get_dotted_name(_test_func), "args": []} job = Job(None, data, None, None) assert job.function_name == "guillotina_amqp.tests.utils._test_func"
def run_command(self, settings=None, loop=None): if loop is not None: self.loop = loop if settings is None: settings = get_settings(self.arguments.configuration, self.arguments.override) if settings.get("loop_policy"): loop_policy = resolve_dotted_name(settings["loop_policy"]) asyncio.set_event_loop_policy(loop_policy()) app = self.make_app(settings) if self.arguments.line_profiler: if not HAS_LINE_PROFILER: sys.stderr.write( "You must first install line_profiler for the --line-profiler option to work.\n" "Use `pip install line_profiler` to install line_profiler.\n" ) return 1 self.line_profiler = line_profiler.LineProfiler() for func in profile.get_profilable_functions(): if fnmatch(get_dotted_name(func), self.arguments.line_profiler_matcher or "*"): self.line_profiler.add_function(func) self.line_profiler.enable_by_count() run_func = self.__run if self.arguments.monitor: if not HAS_AIOMONITOR: sys.stderr.write( "You must install aiomonitor for the " "--monitor option to work.\n" "Use `pip install aiomonitor` to install aiomonitor.\n") return 1 run_func = self.__run_with_monitor if self.arguments.profile: self.profiler = cProfile.Profile() self.profiler.runcall(run_func, app, settings) if self.arguments.profile_output: self.profiler.dump_stats(self.arguments.profile_output) else: # dump to screen self.profiler.print_stats(-1) else: run_func(app, settings) if self.line_profiler is not None: self.line_profiler.disable_by_count() if self.arguments.line_profiler_output: self.line_profiler.dump_stats( self.arguments.line_profiler_output) else: self.line_profiler.print_stats()
def convert_interface_to_schema(iface): properties = {} for name in iface.names(): field = iface[name] try: props = convert_field_to_schema(field) except Exception: logger.warning('Error converting {} of {} into json schema'.format( name, get_dotted_name(iface))) if props is not None: properties[name] = props return properties
def get_task_payload(self): name = self.__parent__.__name__ function = app_settings['hive_tasks'][name] if isinstance(function, dict) and 'klass' in function: function = get_dotted_name(function['klass']) return { 'name': name, 'task_uri': IAbsoluteURL(self)(relative=True), 'function': function, 'args': self.params, 'persistent': True }
def test_job_function_name(): data = { 'func': get_dotted_name(_run_object_task), 'args': ['my.func.foobar'], } job = Job(None, data, None, None) assert job.function_name == 'run_object_task/my.func.foobar' data = { 'func': get_dotted_name(_yield_object_task), 'args': ['my.func.foobar'], } job = Job(None, data, None, None) assert job.function_name == 'yield_object_task/my.func.foobar' data = { 'func': get_dotted_name(_test_func), 'args': [], } job = Job(None, data, None, None) assert job.function_name == 'guillotina_amqp.tests.utils._test_func'
async def __call__(self, request): try: try: view_name = get_dotted_name(request.found_view.view_func) except AttributeError: view_name = get_dotted_name(request.found_view) except AttributeError: view_name = 'unknown' resp = await self.handler(request) metric = metrics.request_summary.labels(method=request.method, view=view_name, response_code=resp.status) try: metric.observe(request.events['finish'] - request.events['start']) except (AttributeError, KeyError): pass return resp
def includeme(root): configure.scan('guillotina_elasticsearch.utility') configure.scan('guillotina_elasticsearch.manager') # add store true to guillotina indexes for name, utility in get_utilities_for(IResourceFactory): if not get_dotted_name(utility._callable).startswith('guillotina.'): continue for field_name, catalog_info in get_index_fields(name).items(): if field_name in ('id', 'path', 'uuid', 'type_name', 'tid', 'creators', 'contributors', 'access_roles', 'access_users', 'parent_uuid', 'title'): catalog_info['store'] = True
def decorator(original_func): key = get_dotted_name(original_func) async def func(*args): if key in _cache: return _cache[key] resp = await original_func(*args) _cache[key] = resp loop = asyncio.get_event_loop() loop.call_later(duration, invalidate, key) return resp return func
def function_name(self): """ """ func = self.get_function_to_run() dotted_name = get_dotted_name(func) if func in [_run_object_task, _yield_object_task]: # Remove guillotina_amqp.utils part dotted_name = dotted_name.lstrip("guillotina_amqp.utils") # Get actuall callable that is passed as the first parameter # of func _callable = self.data.get("args", [""])[0] dotted_name += f"/{_callable}" return dotted_name
def get_service_type_name(self, path): base_path, _, subpath = path.partition('@') if base_path != '/': base_path = base_path.rstrip('/') subpath = subpath.split('/')[0] # could be traversing it... type_name = self._get_type_name(path) if type_name == 'Application': iface = 'guillotina.interfaces.content.IApplication' elif type_name == 'Database': iface = 'guillotina.interfaces.content.IDatabase' else: type_name = self._get_type_name(path) rfactory = getUtility(IResourceFactory, name=type_name) iface = get_dotted_name(rfactory.schema) return base_path, subpath, iface
async def add_object_task(callable=None, ob=None, *args, _request=None, _retries=3, **kwargs): superfunc = _run_object_task if inspect.isasyncgenfunction(callable): # async generators need to be yielded from superfunc = _yield_object_task return await add_task(superfunc, get_dotted_name(callable), get_content_path(ob), *args, _request=_request, _retries=_retries, **kwargs)
def create_apply_task(name, ob, function, request=None, commit=False, args=None, kwargs=None, task_config=None): if task_config is None: task_config = {} if args is None: args = [] if kwargs is None: kwargs = {} from guillotina_hive.model.task import Task if request is None: request = get_current_request() user_data = {} try: participation = request.security.participations[0] user = participation.principal user_data = { 'id': user.id, 'roles': user.roles, 'groups': user.groups, 'Authorization': request.headers.get('Authorization'), 'data': getattr(user, 'data', {}) } except (AttributeError, IndexError): pass if request.container: user_data['container_url'] = IAbsoluteURL(request.container, request)() data = { "name": name, "guillotina": True, "args": { "path": get_full_content_path(request, ob), "function": get_dotted_name(function), 'commit': commit, 'args': args, 'kwargs': kwargs, 'user_data': user_data } } data.update(task_config) task_info = Task(data=data) return task_info
def run_command(self, settings=None, loop=None): if loop is not None: self.loop = loop if settings is None: settings = get_settings(self.arguments.configuration, self.arguments.override) if settings.get('loop_policy'): loop_policy = resolve_dotted_name(settings['loop_policy']) asyncio.set_event_loop_policy(loop_policy()) app = self.make_app(settings) if self.arguments.line_profiler: if not HAS_LINE_PROFILER: sys.stderr.write( 'You must first install line_profiler for the --line-profiler option to work.\n' 'Use `pip install line_profiler` to install line_profiler.\n' ) return 1 self.line_profiler = line_profiler.LineProfiler() for func in profile.get_profilable_functions(): if fnmatch(get_dotted_name(func), self.arguments.line_profiler_matcher or '*'): self.line_profiler.add_function(func) self.line_profiler.enable_by_count() run_func = self.__run if self.arguments.monitor: if not HAS_AIOMONITOR: sys.stderr.write( 'You must install aiomonitor for the ' '--monitor option to work.\n' 'Use `pip install aiomonitor` to install aiomonitor.\n') return 1 run_func = self.__run_with_monitor if self.arguments.profile: self.profiler = cProfile.Profile() self.profiler.runcall(run_func, app, settings) else: run_func(app, settings)
def convert_field_to_schema(field): field_type = type(field) props = {"required": field.required, "title": field.title} if field_type in _array_types: props.update({ "type": "array", "items": { "type": _type_mappings[type(field.value_type)] } }) elif field_type in _type_mappings: props.update({"type": _type_mappings[field_type]}) else: logger.warning( 'Could not convert field {} of {} into json schema'.format( field.__name__, get_dotted_name(field.interface))) return for prop_name, schema_name in _prop_mappings.items(): val = getattr(field, prop_name, None) if val is not None: props[schema_name] = val return props
def _run(self, arguments, settings, app): port = arguments.port or settings.get('address', settings.get('port')) host = arguments.host or settings.get('host', '0.0.0.0') if arguments.line_profiler: if not HAS_LINE_PROFILER: sys.stderr.write( 'You must first install line_profiler for the --line-profiler option to work.\n' 'Use `pip install line_profiler` to install line_profiler.\n' ) return 1 self.line_profiler = line_profiler.LineProfiler() for func in profile.get_profilable_functions(): if fnmatch(get_dotted_name(func), arguments.line_profiler_matcher or '*'): self.line_profiler.add_function(func) self.line_profiler.enable_by_count() if arguments.profile: self.profiler = cProfile.Profile() self.profiler.runcall( web.run_app, app, host=host, port=port, loop=self.get_loop(), access_log_format=settings.get('access_log_format')) else: try: web.run_app( app, host=host, port=port, loop=self.get_loop(), access_log_format=settings.get('access_log_format')) except asyncio.CancelledError: # server shut down, we're good here. pass
def type(self): if hasattr(self._obj, 'type_name'): return self._obj.type_name else: return get_dotted_name(self._obj)
def type(self): return get_dotted_name(self._obj)
async def add_task(func, *args, _request=None, _retries=3, _task_id=None, **kwargs): """Given a function and its arguments, it adds it as a task to be ran by workers. """ # Get the request and prepare request data if _request is None: _request = get_current_request() req_data = { 'url': str(_request.url), 'headers': dict(_request.headers), 'method': _request.method, 'annotations': getattr(_request, 'annotations', {}) } user = get_authenticated_user() if user is not None: try: req_data['user'] = { 'id': user.id, 'roles': [ name for name, setting in user.roles.items() if setting == Allow ], 'groups': user.groups, 'headers': dict(_request.headers), 'data': getattr(user, 'data', {}) } except AttributeError: pass container = task_vars.container.get() if container is not None: req_data['container_url'] = IAbsoluteURL(container, _request)() if _task_id is None: task_id = generate_task_id() else: task_id = _task_id retries = 0 while True: # Get the rabbitmq connection channel, transport, protocol = await amqp.get_connection() try: state = TaskState(task_id) dotted_name = get_dotted_name(func) db = task_vars.db.get() logger.info(f'Scheduling task: {task_id}: {dotted_name}') data = json.dumps({ 'func': dotted_name, 'args': args, 'kwargs': kwargs, 'db_id': getattr(db, 'id', None), 'container_id': getattr(container, 'id', None), 'req_data': req_data, 'task_id': task_id }) # Publish task data on rabbitmq await channel.publish( data, exchange_name=app_settings['amqp']['exchange'], routing_key=app_settings['amqp']['queue'], properties={'delivery_mode': 2}) # Update tasks's global state state_manager = get_state_manager() await update_task_scheduled(state_manager, task_id, updated=time.time()) logger.info(f'Scheduled task: {task_id}: {dotted_name}') return state except (aioamqp.AmqpClosedConnection, aioamqp.exceptions.ChannelClosed): await amqp.remove_connection() if retries >= _retries: raise retries += 1
def run(self): server = get_server() loop = server['loop'] headers = {} for header in self.options.get('headers', '').split(','): if not header: continue name, value = header.split(':') headers[name.strip()] = value.strip() if 'Accept' not in headers: headers['Accept'] = 'application/json' if 'basic_auth' in self.options: encoded = b64encode( self.options['basic_auth'].encode('utf-8')).decode("ascii") headers['Authorization'] = 'Basic {}'.format(encoded) path = self.options.get('path') or '/' ob, tail = loop.run_until_complete(self.get_content(path)) resp = loop.run_until_complete(self.handle_request(headers)) if 'hidden' in self.options: return [] service_definition = {} service_definition.setdefault('description', '') service_definition.setdefault('summary', '') service_definition.setdefault('permission', '') service_definition.setdefault('context', '') raw_service_definition = self.get_service_definition(ob, tail) if raw_service_definition is not None: for key, value in raw_service_definition.items(): if key in ('method', 'permission', 'summary', 'description', 'responses', 'parameters'): if callable(value): value = value(ob) service_definition[key] = value service_definition['context'] = get_dotted_name( raw_service_definition.get('context', Interface)) resp_body = None if resp.headers.get('content-type') == 'application/json': resp_body = loop.run_until_complete(resp.json()) resp_body = _fmt_body(resp_body, 8) else: resp_body = loop.run_until_complete(resp.text()) content = { 'path_spec': (self.options.get('path_spec') or self.options.get('method', 'GET').upper()), 'request': { 'method': self.options.get('method', 'GET').upper(), 'method_lower': self.options.get('method', 'GET').lower(), 'path': self.options.get('path') or '/', 'headers': _fmt_headers(_clean_headers(headers), 8), 'body': _fmt_body(self.options.get('body'), 8) }, 'response': { 'status': resp.status, 'headers': _fmt_headers(_clean_headers(dict(resp.headers)), 8), 'body': resp_body }, 'service': service_definition, } rst_content = """.. http:{request[method_lower]}:: {path_spec} {service[summary]} {service[description]} - Permission: **{service[permission]}** - Context: **{service[context]}** .. http:example:: curl httpie {request[method]} {request[path]} HTTP/1.1 {request[headers]} {request[body]} HTTP/1.1 {response[status]} OK {response[headers]} {response[body]} """ rst_content = rst_content.format(**content) for parameter in service_definition.get('parameters', []): if isinstance(parameter, str): parameter = {'name': parameter, 'type': 'string'} if parameter.get('in', 'query') != 'query': continue parameter.setdefault('description', '') if parameter.get('required'): parameter['description'] += ' (required)' elif parameter.get('default'): parameter['description'] += ' (default: {default})'.format( **parameter) rst_content += '\n :query {type} {name}: {description}'.format( **parameter) responses = { code: info['description'] for code, info in service_definition.get('responses', {}).items() } responses.setdefault('401', 'You are not authorized to perform the operation') responses.setdefault('404', 'The resource does not exist') for code in sorted(responses): rst_content += ('\n :statuscode {}: {}'.format( code, responses[code])) rst_content = rst_content.split("\n") view = docutils.statemachine.StringList(rst_content, '<gapi>') node = nodes.paragraph() self.state.nested_parse(view, 0, node) return [node]
def __init__(self, view): self.__module__ = view.__module__ self.__qualname__ = get_dotted_name(view).split(".")[-1]
def run(self): server = get_server() loop = server["loop"] headers = {} for header in self.options.get("headers", "").split(","): if not header: continue name, value = header.split(":") headers[name.strip()] = value.strip() if "Accept" not in headers: headers["Accept"] = "application/json" if "basic_auth" in self.options: encoded = b64encode( self.options["basic_auth"].encode("utf-8")).decode("ascii") headers["Authorization"] = "Basic {}".format(encoded) path = self.options.get("path") or "/" ob, tail = loop.run_until_complete(self.get_content(path)) resp = loop.run_until_complete(self.handle_request(headers)) if "hidden" in self.options: return [] service_definition = {} service_definition.setdefault("description", "") service_definition.setdefault("summary", "") service_definition.setdefault("permission", "") service_definition.setdefault("context", "") raw_service_definition = self.get_service_definition(ob, tail) if raw_service_definition is not None: for key, value in raw_service_definition.items(): if key in ("method", "permission", "summary", "description", "responses", "parameters"): if callable(value): value = value(ob) service_definition[key] = value service_definition["context"] = get_dotted_name( raw_service_definition.get("context", Interface)) resp_body = None if resp.headers.get("content-type") == "application/json": resp_body = loop.run_until_complete(resp.json()) resp_body = _fmt_body(resp_body, 8) else: resp_body = loop.run_until_complete(resp.text()) content = { "path_spec": (self.options.get("path_spec") or self.options.get("method", "GET").upper()), "request": { "method": self.options.get("method", "GET").upper(), "method_lower": self.options.get("method", "GET").lower(), "path": self.options.get("path") or "/", "headers": _fmt_headers(_clean_headers(headers), 8), "body": _fmt_body(self.options.get("body"), 8), }, "response": { "status": resp.status_code, "headers": _fmt_headers(_clean_headers(dict(resp.headers)), 8), "body": resp_body, }, "service": service_definition, } rst_content = """.. http:{request[method_lower]}:: {path_spec} {service[summary]} {service[description]} - Permission: **{service[permission]}** - Context: **{service[context]}** .. http:example:: curl httpie {request[method]} {request[path]} HTTP/1.1 {request[headers]} {request[body]} HTTP/1.1 {response[status]} OK {response[headers]} {response[body]} """ rst_content = rst_content.format(**content) for parameter in service_definition.get("parameters", []): if isinstance(parameter, str): parameter = {"name": parameter, "type": "string"} if parameter.get("in", "query") != "query": continue parameter.setdefault("description", "") if parameter.get("required"): parameter["description"] += " (required)" elif parameter.get("default"): parameter["description"] += " (default: {default})".format( **parameter) try: if "schema" in parameter: ptype = parameter["schema"]["type"] else: ptype = parameter.get("type", "string") rst_content += "\n :query {ptype} {name}: {description}".format( ptype=ptype, **parameter) except (KeyError, ValueError): logger.warning(f"Could not format paremeter: {parameter}") responses = { code: info["description"] for code, info in service_definition.get("responses", {}).items() } responses.setdefault( "401", "You are not authorized to perform the operation") responses.setdefault("404", "The resource does not exist") for code in sorted(responses): rst_content += "\n :statuscode {}: {}".format( code, responses[code]) rst_content = rst_content.split("\n") view = docutils.statemachine.StringList(rst_content, "<gapi>") node = nodes.paragraph() self.state.nested_parse(view, 0, node) return [node]
async def add_task(func, *args, _request=None, _retries=3, _task_id=None, **kwargs): """Given a function and its arguments, it adds it as a task to be ran by workers. """ # Get the request and prepare request data if _request is None: _request = get_current_request() req_data: SerializedRequest = serialize_request(_request) if _task_id is None: task_id = generate_task_id() else: task_id = _task_id dotted_name = get_dotted_name(func) retries = 0 while True: # Get the rabbitmq connection try: channel, transport, protocol = await amqp.get_connection() except AMQPConfigurationNotFoundError: logger.warning( f"Could not schedule {dotted_name}, AMQP settings not configured" ) return try: state = TaskState(task_id) db = task_vars.db.get() container = task_vars.container.get() logger.info(f"Scheduling task: {task_id}: {dotted_name}") data = json.dumps({ "func": dotted_name, "args": args, "kwargs": kwargs, "db_id": getattr(db, "id", None), "container_id": getattr(container, "id", None), "req_data": req_data, "task_id": task_id, }) # Publish task data on rabbitmq await channel.publish( data, exchange_name=app_settings["amqp"]["exchange"], routing_key=app_settings["amqp"]["queue"], properties={"delivery_mode": 2}, ) # Update tasks's global state state_manager = get_state_manager() await update_task_scheduled(state_manager, task_id, updated=time.time()) logger.info(f"Scheduled task: {task_id}: {dotted_name}") return state except (aioamqp.AmqpClosedConnection, aioamqp.exceptions.ChannelClosed): await amqp.remove_connection() if retries >= _retries: raise retries += 1
def type(self): return get_dotted_name(self._obj)
def api(self, path, method='get', file_type_name=None, **kwargs): if path != '/': path = path.rstrip('/') print('Getting {} {}'.format(method, path)) kwargs['auth'] = ('root', 'root') kwargs['headers'] = DEFAULT_HEADERS.copy() kwargs['headers'].update(kwargs.get('headers', {})) url = self._base_url + path response = getattr(requests, method)(url, **kwargs) self._store_type_name(response, url, kwargs) service = self.get_service_def(path, method.upper()) # path scheme used for things like traversing to registry value... name = service.get('name') path_scheme = kwargs.pop('path_scheme', name) iface = resolve_dotted_name(self.get_service_type_name(path)[-1]) dotted = get_dotted_name(iface) responses = self._function_swagger_param(service.get('responses'), url) parameters = self._function_swagger_param(service.get('parameters'), url) data = { 'path': path, 'path_scheme': path_scheme, 'method': method, 'options': kwargs, 'request': self.dump_request(response.request), 'response': self.dump_response(response), 'service': { 'name': service.get('name'), 'title': service.get('title'), 'summary': service.get('summary'), 'parameters': parameters, 'responses': responses, 'method': service.get('method', 'GET'), 'context': get_dotted_name(iface), 'permission': service.get('permission') } } if file_type_name is None: file_type_name = dotted.split('.')[-1][1:].lower() filepath = '{}/{}-{}'.format(self._output_dir, file_type_name, method.lower()) if path_scheme != name: # use special name here... name, _, rest = path_scheme.partition('/') filepath += '-{}:{}'.format( name.replace('@', ''), ''.join([l for l in rest.split(':')[0] if l not in '[]():-'])) elif service.get('name'): filepath += '-' + service.get('name').replace('@', '') filepath += '.json' fi = open(filepath, 'w') fi.write(json.dumps(data, indent=4, sort_keys=True)) fi.close()
def type(self): if hasattr(self._obj, 'type_name'): return self._obj.type_name else: return get_dotted_name(self._obj)
def run(self): server = get_server() loop = server['loop'] headers = {} for header in self.options.get('headers', '').split(','): if not header: continue name, value = header.split(':') headers[name.strip()] = value.strip() if 'Accept' not in headers: headers['Accept'] = 'application/json' if 'basic_auth' in self.options: encoded = b64encode( self.options['basic_auth'].encode('utf-8')).decode("ascii") headers['Authorization'] = 'Basic {}'.format(encoded) path = self.options.get('path') or '/' ob, tail = loop.run_until_complete(self.get_content(path)) resp = loop.run_until_complete(self.handle_request(headers)) if 'hidden' in self.options: return [] service_definition = {} service_definition.setdefault('description', '') service_definition.setdefault('summary', '') service_definition.setdefault('permission', '') service_definition.setdefault('context', '') raw_service_definition = self.get_service_definition(ob, tail) if raw_service_definition is not None: for key, value in raw_service_definition.items(): if key in ('method', 'permission', 'summary', 'description', 'responses', 'parameters'): if callable(value): value = value(ob) service_definition[key] = value service_definition['context'] = get_dotted_name( raw_service_definition.get('context', Interface)) resp_body = None if resp.headers.get('content-type') == 'application/json': resp_body = loop.run_until_complete(resp.json()) resp_body = _fmt_body(resp_body, 8) else: resp_body = loop.run_until_complete(resp.text()) content = { 'path_spec': (self.options.get('path_spec') or self.options.get('method', 'GET').upper()), 'request': { 'method': self.options.get('method', 'GET').upper(), 'method_lower': self.options.get('method', 'GET').lower(), 'path': self.options.get('path') or '/', 'headers': _fmt_headers(_clean_headers(headers), 8), 'body': _fmt_body(self.options.get('body'), 8) }, 'response': { 'status': resp.status, 'headers': _fmt_headers(_clean_headers(dict(resp.headers)), 8), 'body': resp_body }, 'service': service_definition, } rst_content = """.. http:{request[method_lower]}:: {path_spec} {service[summary]} {service[description]} - Permission: **{service[permission]}** - Context: **{service[context]}** .. http:example:: curl httpie {request[method]} {request[path]} HTTP/1.1 {request[headers]} {request[body]} HTTP/1.1 {response[status]} OK {response[headers]} {response[body]} """ rst_content = rst_content.format(**content) for parameter in service_definition.get('parameters', []): if isinstance(parameter, str): parameter = {'name': parameter, 'type': 'string'} if parameter.get('in', 'query') != 'query': continue parameter.setdefault('description', '') if parameter.get('required'): parameter['description'] += ' (required)' elif parameter.get('default'): parameter['description'] += ' (default: {default})'.format( **parameter) rst_content += '\n :query {type} {name}: {description}'.format( **parameter) responses = { code: info['description'] for code, info in service_definition.get('responses', {}).items() } responses.setdefault('401', 'You are not authorized to perform the operation') responses.setdefault('404', 'The resource does not exist') for code in sorted(responses): rst_content += ('\n :statuscode {}: {}'.format( code, responses[code])) rst_content = rst_content.split("\n") view = docutils.statemachine.StringList(rst_content, '<gapi>') node = nodes.paragraph() self.state.nested_parse(view, 0, node) return [node]