async def fetch_blueprint_from_github_url(opp: OpenPeerPower, url: str) -> ImportedBlueprint: """Get a blueprint from a github url.""" import_url = _get_github_import_url(url) session = aiohttp_client.async_get_clientsession(opp) resp = await session.get(import_url, raise_for_status=True) raw_yaml = await resp.text() data = yaml.parse_yaml(raw_yaml) blueprint = Blueprint(data) parsed_import_url = yarl.URL(import_url) suggested_filename = f"{parsed_import_url.parts[1]}/{parsed_import_url.parts[-1]}" if suggested_filename.endswith(".yaml"): suggested_filename = suggested_filename[:-5] return ImportedBlueprint(suggested_filename, raw_yaml, blueprint)
def _extract_blueprint_from_community_topic( url: str, topic: dict, ) -> ImportedBlueprint | None: """Extract a blueprint from a community post JSON. Async friendly. """ block_content = None blueprint = None post = topic["post_stream"]["posts"][0] for match in COMMUNITY_CODE_BLOCK.finditer(post["cooked"]): block_syntax, block_content = match.groups() if block_syntax not in ("auto", "yaml"): continue block_content = html.unescape(block_content.strip()) try: data = yaml.parse_yaml(block_content) except OpenPeerPowerError: if block_syntax == "yaml": raise continue if not is_blueprint_config(data): continue blueprint = Blueprint(data) break if blueprint is None: raise OpenPeerPowerError( "No valid blueprint found in the topic. Blueprint syntax blocks need to be marked as YAML or no syntax." ) return ImportedBlueprint(f'{post["username"]}/{topic["slug"]}', block_content, blueprint)
async def fetch_blueprint_from_github_gist_url(opp: OpenPeerPower, url: str) -> ImportedBlueprint: """Get a blueprint from a Github Gist.""" if not url.startswith("https://gist.github.com/"): raise UnsupportedUrl("Not a GitHub gist url") parsed_url = yarl.URL(url) session = aiohttp_client.async_get_clientsession(opp) resp = await session.get( f"https://api.github.com/gists/{parsed_url.parts[2]}", headers={"Accept": "application/vnd.github.v3+json"}, raise_for_status=True, ) gist = await resp.json() blueprint = None filename = None content = None for filename, info in gist["files"].items(): if not filename.endswith(".yaml"): continue content = info["content"] data = yaml.parse_yaml(content) if not is_blueprint_config(data): continue blueprint = Blueprint(data) break if blueprint is None: raise OpenPeerPowerError( "No valid blueprint found in the gist. The blueprint file needs to end with '.yaml'" ) return ImportedBlueprint(f"{gist['owner']['login']}/{filename[:-5]}", content, blueprint)
async def ws_save_blueprint(opp, connection, msg): """Save a blueprint.""" path = msg["path"] domain = msg["domain"] domain_blueprints: dict[str, models.DomainBlueprints] | None = opp.data.get( DOMAIN, {}) if domain not in domain_blueprints: connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT, "Unsupported domain") try: blueprint = models.Blueprint(yaml.parse_yaml(msg["yaml"]), expected_domain=domain) if "source_url" in msg: blueprint.update_metadata(source_url=msg["source_url"]) except OpenPeerPowerError as err: connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT, str(err)) return try: await domain_blueprints[domain].async_add_blueprint(blueprint, path) except FileAlreadyExists: connection.send_error(msg["id"], "already_exists", "File already exists") return except OSError as err: connection.send_error(msg["id"], websocket_api.ERR_UNKNOWN_ERROR, str(err)) return connection.send_result(msg["id"], )
def test_input(): """Test loading inputs.""" data = {"hello": yaml.Input("test_name")} assert yaml.parse_yaml(yaml.dump(data)) == data