def registry_logout() -> None: """ Logout from Registry account. :return: None """ request_api("POST", "/rest-auth/logout/")
def test_request_api_500(self, request_mock): """Test for request_api method 500 server response.""" resp_mock = mock.Mock() resp_mock.status_code = 500 request_mock.return_value = resp_mock with self.assertRaises(ClickException): request_api("GET", "/path")
def test_request_api_unexpected_response(self, request_mock): """Test for fetch_package method unexpected sever response.""" resp_mock = mock.Mock() resp_mock.status_code = 500 request_mock.return_value = resp_mock with self.assertRaises(ClickException): request_api('GET', '/path')
def test_request_api_403(self, request_mock): """Test for fetch_package method not authorized sever response.""" resp_mock = mock.Mock() resp_mock.status_code = 403 request_mock.return_value = resp_mock with self.assertRaises(ClickException): request_api('GET', '/path')
def test_request_api_409(self, request_mock): """Test for request_api method conflict server response.""" resp_mock = mock.Mock() resp_mock.status_code = 409 resp_mock.json = lambda: {"detail": "some"} request_mock.return_value = resp_mock with self.assertRaises(ClickException): request_api("GET", "/path")
def test_request_api_unexpected_response(self, request_mock): """Test for request_api method unexpected server response.""" resp_mock = mock.Mock() resp_mock.status_code = 501 # not implemented status resp_mock.json = _raise_json_decode_error request_mock.return_value = resp_mock with self.assertRaises(ClickException): request_api("GET", "/path")
def registry_reset_password(email: str) -> None: """ Request Registry to reset password. :param email: user email. :return: None. """ request_api("POST", "/rest-auth/password/reset/", data={"email": email})
def search_items(ctx: Context, item_type: str, query: str, page: int) -> Tuple[List[Dict], int]: """ Search items by query and click.echo results. :param ctx: Context object. :param item_type: item type. :param query: query string. :return: (List of items, int items total count). """ click.echo('Searching for "{}"...'.format(query)) item_type_plural = item_type + "s" if ctx.config.get("is_local"): results = _search_items_locally(ctx, item_type_plural) count = len(results) else: resp = cast( JSONLike, request_api( "GET", "/{}".format(item_type_plural), params={ "search": query, "page": page }, ), ) results = cast(List[Dict], resp["results"]) count = cast(int, resp["count"]) return results, count
def register(username: str, email: str, password: str, password_confirmation: str) -> str: """ Register new Registry account and automatically login if successful. :param username: str username. :param email: str email. :param password: str password. :param password_confirmation: str password confirmation. :return: str auth token. """ data = { "username": username, "email": email, "password1": password, "password2": password_confirmation, } resp_json, status_code = request_api( "POST", "/rest-auth/registration/", data=data, handle_400=False, return_code=True, ) if status_code == 400: errors: List[str] = [] for key in ("username", "email", "password1", "password2"): param_errors = resp_json.get(key) if param_errors: errors.extend(param_errors) raise ClickException("Errors occured during registration.\n" + "\n".join(errors)) return resp_json["key"]
def publish_agent(ctx: Context): """Publish an agent.""" try_to_load_agent_config(ctx) check_is_author_logged_in(ctx.agent_config.author) name = ctx.agent_config.agent_name agent_config_path = os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE) output_tar = os.path.join(ctx.cwd, "{}.tar.gz".format(name)) _compress(output_tar, agent_config_path) data = { "name": name, "description": ctx.agent_config.description, "version": ctx.agent_config.version, "connections": ctx.agent_config.connections, "protocols": ctx.agent_config.protocols, "skills": ctx.agent_config.skills, } path = "/agents/create" logger.debug("Publishing agent {} to Registry ...".format(name)) resp = request_api("POST", path, data=data, is_auth=True, filepath=output_tar) click.echo( "Successfully published agent {} to the Registry. Public ID: {}". format(name, resp["public_id"]))
def fetch_agent(ctx: Context, public_id: PublicId) -> None: """ Fetch Agent from Registry. :param public_id: str public ID of desirable Agent. :return: None """ author, name, version = public_id.author, public_id.name, public_id.version api_path = "/agents/{}/{}/{}".format(author, name, version) resp = request_api("GET", api_path) file_url = resp["file"] target_folder = os.path.join(ctx.cwd, name) os.makedirs(target_folder, exist_ok=True) click.echo("Fetching dependencies...") for item_type in ("connection", "skill", "protocol"): item_type_plural = item_type + "s" for item_public_id in resp[item_type_plural]: item_public_id = PublicId.from_str(item_public_id) try: fetch_package(item_type, item_public_id, target_folder) except Exception as e: rmtree(target_folder) raise click.ClickException( 'Unable to fetch dependency for agent "{}", aborting. {}'.format( name, e ) ) click.echo("Dependencies successfully fetched.") filepath = download_file(file_url, ctx.cwd) extract(filepath, target_folder) click.echo("Agent {} successfully fetched to {}.".format(name, target_folder))
def fetch_package(obj_type: str, public_id: PublicId, cwd: str, dest: str) -> Path: """ Fetch a package (connection/contract/protocol/skill) from Registry. :param obj_type: str type of object you want to fetch: 'connection', 'protocol', 'skill' :param public_id: str public ID of object. :param cwd: str path to current working directory. :return: package path """ logger.debug("Fetching {obj_type} {public_id} from Registry...".format( public_id=public_id, obj_type=obj_type)) author, name, version = public_id.author, public_id.name, public_id.version item_type_plural = obj_type + "s" # used for API and folder paths api_path = "/{}/{}/{}/{}".format(item_type_plural, author, name, version) resp = request_api("GET", api_path) file_url = resp["file"] logger.debug("Downloading {obj_type} {public_id}...".format( public_id=public_id, obj_type=obj_type)) filepath = download_file(file_url, cwd) # next code line is needed because the items are stored in tarball packages as folders dest = os.path.split(dest)[ 0] # TODO: replace this hotfix with a proper solution logger.debug("Extracting {obj_type} {public_id}...".format( public_id=public_id, obj_type=obj_type)) extract(filepath, dest) click.echo("Successfully fetched {obj_type}: {public_id}.".format( public_id=public_id, obj_type=obj_type)) package_path = os.path.join(dest, public_id.name) return Path(package_path)
def protocols(ctx: Context, query): """Search for Protocols.""" if ctx.config.get("is_registry"): click.echo('Searching for "{}"...'.format(query)) resp = request_api("GET", "/protocols", params={"search": query}) if len(resp) == 0: click.echo("No protocols found.") # pragma: no cover else: click.echo("Protocols found:\n") click.echo(_format_items(resp)) return registry = cast(str, ctx.config.get("registry_directory")) result = [] # type: List[Dict] _get_details_from_dir( ctx.protocol_loader, AEA_DIR, "protocols", DEFAULT_PROTOCOL_CONFIG_FILE, result ) _get_details_from_dir( ctx.protocol_loader, registry, "*/protocols", DEFAULT_PROTOCOL_CONFIG_FILE, result, ) print("Available protocols:") print(_format_items(sorted(result, key=lambda k: k["name"])))
def publish_agent(ctx: Context): """Publish an agent.""" try_to_load_agent_config(ctx) check_is_author_logged_in(ctx.agent_config.author) name = ctx.agent_config.agent_name config_file_source_path = os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE) output_tar = os.path.join(ctx.cwd, "{}.tar.gz".format(name)) with tempfile.TemporaryDirectory() as temp_dir: package_dir = os.path.join(temp_dir, name) os.makedirs(package_dir) config_file_target_path = os.path.join(package_dir, DEFAULT_AEA_CONFIG_FILE) shutil.copy(config_file_source_path, config_file_target_path) _compress(output_tar, package_dir) data = { "name": name, "description": ctx.agent_config.description, "version": ctx.agent_config.version, "connections": ctx.agent_config.connections, "contracts": ctx.agent_config.contracts, "protocols": ctx.agent_config.protocols, "skills": ctx.agent_config.skills, } path = "/agents/create" logger.debug("Publishing agent {} to Registry ...".format(name)) resp = request_api("POST", path, data=data, is_auth=True, filepath=output_tar) click.echo( "Successfully published agent {} to the Registry. Public ID: {}".format( name, resp["public_id"] ) )
def download_package(package_id: PackageId, destination_path: str) -> None: """Download a package into a directory.""" api_path = f"/{package_id.package_type.to_plural()}/{package_id.author}/{package_id.name}/{package_id.public_id.LATEST_VERSION}" resp = cast(JSONLike, request_api("GET", api_path)) file_url = cast(str, resp["file"]) filepath = download_file(file_url, destination_path) extract(filepath, destination_path)
def fetch_agent( click_context, public_id: PublicId, alias: Optional[str] = None ) -> None: """ Fetch Agent from Registry. :param ctx: Context :param public_id: str public ID of desirable Agent. :param click_context: the click context. :param alias: an optional alias. :return: None """ author, name, version = public_id.author, public_id.name, public_id.version api_path = "/agents/{}/{}/{}".format(author, name, version) resp = request_api("GET", api_path) file_url = resp["file"] ctx = cast(Context, click_context.obj) filepath = download_file(file_url, ctx.cwd) folder_name = name if alias is None else alias aea_folder = os.path.join(ctx.cwd, folder_name) ctx.clean_paths.append(aea_folder) extract(filepath, ctx.cwd) if alias is not None: os.rename(name, alias) ctx.cwd = aea_folder try_to_load_agent_config(ctx) if alias is not None: ctx.agent_config.agent_name = alias ctx.agent_loader.dump( ctx.agent_config, open(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w") ) click.echo("Fetching dependencies...") for item_type in ("connection", "contract", "skill", "protocol"): item_type_plural = item_type + "s" # initialize fetched agent with empty folders for custom packages custom_items_folder = os.path.join(ctx.cwd, item_type_plural) os.makedirs(custom_items_folder) config = getattr(ctx.agent_config, item_type_plural) for item_public_id in config: try: _add_item(click_context, item_type, item_public_id) except Exception as e: raise click.ClickException( 'Unable to fetch dependency for agent "{}", aborting. {}'.format( name, e ) ) click.echo("Dependencies successfully fetched.") click.echo("Agent {} successfully fetched to {}.".format(name, aea_folder))
def push_item(ctx: Context, item_type: str, item_id: PublicId) -> None: """ Push item to the Registry. :param item_type: str type of item (connection/protocol/skill). :param item_id: str item name. :return: None """ item_type_plural = item_type + "s" items_folder = os.path.join(ctx.cwd, item_type_plural) item_path = os.path.join(items_folder, item_id.name) item_config_filepath = os.path.join(item_path, "{}.yaml".format(item_type)) logger.debug("Reading {} {} config ...".format(item_id.name, item_type)) item_config = load_yaml(item_config_filepath) check_is_author_logged_in(item_config["author"]) logger.debug("Searching for {} {} in {} ...".format( item_id.name, item_type, items_folder)) if not os.path.exists(item_path): raise click.ClickException( '{} "{}" not found in {}. Make sure you run push command ' "from a correct folder.".format(item_type.title(), item_id.name, items_folder)) output_filename = "{}.tar.gz".format(item_id.name) logger.debug("Compressing {} {} to {} ...".format(item_id.name, item_type, output_filename)) _compress_dir(output_filename, item_path) output_filepath = os.path.join(ctx.cwd, output_filename) data = { "name": item_id.name, "description": item_config["description"], "version": item_config["version"], } # dependencies for key in ["connections", "contracts", "protocols", "skills"]: deps_list = item_config.get(key) if deps_list: data.update({key: deps_list}) path = "/{}/create".format(item_type_plural) logger.debug("Pushing {} {} to Registry ...".format( item_id.name, item_type)) resp = request_api("POST", path, data=data, is_auth=True, filepath=output_filepath) click.echo( "Successfully pushed {} {} to the Registry. Public ID: {}".format( item_type, item_id.name, resp["public_id"]))
def test_request_api_201(self, request_mock): """Test for request_api method 201 server response.""" expected_result = {"correct": "json"} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 201 request_mock.return_value = resp_mock result = request_api("GET", "/path") self.assertEqual(result, expected_result)
def test_request_api_with_file_positive(self, request_mock): """Test for request_api method with file positive result.""" expected_result = {"correct": "json"} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 200 request_mock.return_value = resp_mock result = request_api("GET", "/path", filepath="filepath") self.assertEqual(result, expected_result)
def registry_login(username: str, password: str) -> str: """ Login into Registry account. :param username: str username. :param password: str password. :return: str token """ resp = request_api( "POST", "/rest-auth/login/", data={"username": username, "password": password} ) return resp["key"]
def test_request_api_positive(self, request_mock): """Test for request_api method positive result.""" expected_result = {"correct": "json"} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 200 request_mock.return_value = resp_mock result = request_api("GET", "/path") request_mock.assert_called_once_with( method="GET", params=None, data=None, files=None, headers={}, url=REGISTRY_API_URL + "/path", ) self.assertEqual(result, expected_result) result = request_api("GET", "/path", return_code=True) self.assertEqual(result, (expected_result, 200))
def test_request_api_with_auth_positive(self, get_or_create_cli_config_mock, request_mock): """Test for request_api method with auth positive result.""" expected_result = {"correct": "json"} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 200 request_mock.return_value = resp_mock result = request_api("GET", "/path", is_auth=True) self.assertEqual(result, expected_result)
def agents(ctx: Context, query): """Search for Agents.""" click.echo('Searching for "{}"...'.format(query)) if ctx.config.get("is_local"): results = _search_items(ctx, "agents") else: results = request_api("GET", "/agents", params={"search": query}) if not len(results): click.echo("No agents found.") # pragma: no cover else: click.echo("Agents found:\n") click.echo(_format_items(results))
def test_request_api_positive(self, request_mock): """Test for fetch_package method positive result.""" expected_result = {'correct': 'json'} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 200 request_mock.return_value = resp_mock result = request_api('GET', '/path') request_mock.assert_called_once_with(method='GET', params=None, url=REGISTRY_API_URL + '/path') self.assertEqual(result, expected_result)
def publish_agent(ctx: Context) -> None: """Publish an agent.""" try_to_load_agent_config(ctx) check_is_author_logged_in(ctx.agent_config.author) name = ctx.agent_config.agent_name config_file_source_path = os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE) readme_source_path = os.path.join(ctx.cwd, DEFAULT_README_FILE) output_tar = os.path.join(ctx.cwd, "{}.tar.gz".format(name)) with tempfile.TemporaryDirectory() as temp_dir: package_dir = os.path.join(temp_dir, name) os.makedirs(package_dir) config_file_target_path = os.path.join(package_dir, DEFAULT_AEA_CONFIG_FILE) shutil.copy(config_file_source_path, config_file_target_path) if is_readme_present(readme_source_path): readme_file_target_path = os.path.join(package_dir, DEFAULT_README_FILE) shutil.copy(readme_source_path, readme_file_target_path) _compress(output_tar, package_dir) data = { "name": name, "description": ctx.agent_config.description, "version": ctx.agent_config.version, CONNECTIONS: ctx.agent_config.connections, CONTRACTS: ctx.agent_config.contracts, PROTOCOLS: ctx.agent_config.protocols, SKILLS: ctx.agent_config.skills, } files = {} try: files["file"] = open(output_tar, "rb") if is_readme_present(readme_source_path): files["readme"] = open(readme_source_path, "rb") path = "/agents/create" logger.debug("Publishing agent {} to Registry ...".format(name)) resp = cast( JSONLike, request_api("POST", path, data=data, is_auth=True, files=files) ) finally: for fd in files.values(): fd.close() click.echo( "Successfully published agent {} to the Registry. Public ID: {}".format( name, resp["public_id"] ) )
def test_request_api_with_files_positive(self, request_mock): """Test for request_api method with file positive result.""" expected_result = {"correct": "json"} resp_mock = mock.Mock() resp_mock.json = lambda: expected_result resp_mock.status_code = 200 request_mock.return_value = resp_mock test_files = { "file": open("file.tar.gz", "rb"), "readme": open("file.md", "rb"), } result = request_api("GET", "/path", files=test_files) self.assertEqual(result, expected_result)
def search_items(ctx: Context, item_type: str, query: str) -> List: """ Search items by query and click.echo results. :param ctx: Context object. :param item_type: item type. :param query: query string. :return: None """ click.echo('Searching for "{}"...'.format(query)) item_type_plural = item_type + "s" if ctx.config.get("is_local"): return _search_items_locally(ctx, item_type_plural) else: return request_api("GET", "/{}".format(item_type_plural), params={"search": query})
def agents(ctx: Context, query): """Search for Agents.""" if ctx.config.get("is_registry"): resp = request_api("GET", "/agents", params={"search": query}) if len(resp) == 0: click.echo("No agents found.") # pragma: no cover else: click.echo("Agents found:\n") click.echo(_format_items(resp)) return else: registry = cast(str, ctx.config.get("registry_directory")) result = [] # type: List[Dict] _get_details_from_dir( ctx.agent_loader, registry, "agents", DEFAULT_AEA_CONFIG_FILE, result ) print("Available agents:") print(_format_items(sorted(result, key=lambda k: k["name"])))
def connections(ctx: Context, query): """Search for Connections.""" if ctx.config.get("is_registry"): click.echo('Searching for "{}"...'.format(query)) resp = request_api('GET', '/connections', params={'search': query}) if not len(resp): click.echo('No connections found.') else: click.echo('Connections found:\n') click.echo(format_items(resp)) return registry = cast(str, ctx.config.get("registry")) result: List[Dict] = [] _get_details_from_dir(ctx.connection_loader, AEA_DIR, "connections", DEFAULT_CONNECTION_CONFIG_FILE, result) _get_details_from_dir(ctx.connection_loader, registry, "connections", DEFAULT_CONNECTION_CONFIG_FILE, result) print("Available connections:") print(format_items(sorted(result, key=lambda k: k['name'])))
def fetch_agent(ctx: Context, public_id: PublicId, click_context) -> None: """ Fetch Agent from Registry. :param public_id: str public ID of desirable Agent. :return: None """ author, name, version = public_id.author, public_id.name, public_id.version api_path = "/agents/{}/{}/{}".format(author, name, version) resp = request_api("GET", api_path) file_url = resp["file"] filepath = download_file(file_url, ctx.cwd) extract(filepath, ctx.cwd) target_folder = os.path.join(ctx.cwd, name) ctx.cwd = target_folder try_to_load_agent_config(ctx) click.echo("Fetching dependencies...") for item_type in ("connection", "contract", "skill", "protocol"): item_type_plural = item_type + "s" # initialize fetched agent with empty folders for custom packages custom_items_folder = os.path.join(ctx.cwd, item_type_plural) os.makedirs(custom_items_folder) config = getattr(ctx.agent_config, item_type_plural) for item_public_id in config: try: _add_item(click_context, item_type, item_public_id) except Exception as e: rmtree(target_folder) raise click.ClickException( 'Unable to fetch dependency for agent "{}", aborting. {}'. format(name, e)) click.echo("Dependencies successfully fetched.") click.echo("Agent {} successfully fetched to {}.".format( name, target_folder))