From 8dd891aced61f6a12193970f78012d81ea9b2ee1 Mon Sep 17 00:00:00 2001 From: Ian Pittwood Date: Mon, 8 Apr 2019 15:57:44 -0500 Subject: [PATCH] Remove flask YAML web editor from Spyglass Spyglass was originally built with a web-based YAML editor. Unfortunately, the presence of the editor has caused some problems. It can cause the Docker container to freeze before creating the intermediary YAML file. The execution of the flask app is also the root cause for the Bandit B104 errors and B605 error. Since the target audience for Spyglass is developers, it can be assumed that they will have access to an editor with support for YAML files. Having a web-based version of the YAML editor is unnecessary and will just result in more code to maintain in the future. Removes the editor script from utils. Removes the yaml-editor entry point from the package. Removes references to the yaml-editor from the CLI and engine. Resolves all known Bandit errors. In the future, a pause in execution could be provided to allow users to make quick edits. Log messages could also notify users when a placeholder value is inserted in the intermediary so they can fix it. Change-Id: Ibc37e61f93b33904ea839e12fe5a8d586985e0b1 --- MANIFEST.in | 2 - doc/source/getting_started.rst | 4 - doc/source/index.rst | 1 - doc/source/yaml-editor.rst | 65 ------- setup.py | 3 - spyglass/parser/engine.py | 23 +-- spyglass/spyglass.py | 11 +- spyglass/utils/editor/__init__.py | 0 spyglass/utils/editor/editor.py | 170 ------------------ spyglass/utils/editor/static/app.js | 92 ---------- spyglass/utils/editor/static/favicon.ico | Bin 1150 -> 0 bytes .../editor/static/jquery-linedtextarea.css | 68 ------- .../editor/static/jquery-linedtextarea.js | 126 ------------- spyglass/utils/editor/static/js-yaml.min.js | 1 - spyglass/utils/editor/templates/yaml.html | 38 ---- 15 files changed, 3 insertions(+), 601 deletions(-) delete mode 100644 doc/source/yaml-editor.rst delete mode 100644 spyglass/utils/editor/__init__.py delete mode 100644 spyglass/utils/editor/editor.py delete mode 100644 spyglass/utils/editor/static/app.js delete mode 100644 spyglass/utils/editor/static/favicon.ico delete mode 100755 spyglass/utils/editor/static/jquery-linedtextarea.css delete mode 100755 spyglass/utils/editor/static/jquery-linedtextarea.js delete mode 100644 spyglass/utils/editor/static/js-yaml.min.js delete mode 100644 spyglass/utils/editor/templates/yaml.html diff --git a/MANIFEST.in b/MANIFEST.in index e00b83e..d6f43a3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,2 @@ -recursive-include spyglass/utils/editor/static * -recursive-include spyglass/utils/editor/templates * recursive-include spyglass/ **.yaml recursive-include spyglass/ **.json diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index bca3532..ef1df4a 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -72,9 +72,6 @@ Supported Features 2. Remote Data Source Plugin: Supports extracting site data from a REST endpoint. -3. YAML Editor for Intermediary YAML: Support runtime editing of missing - site parameters, see :ref:`yaml-editor-info` - Future Work ----------- 1) Schema based manifest generation instead of Jinja2 templates. It shall @@ -160,7 +157,6 @@ Options: excel spec -idir, --intermediary_dir PATH The path where intermediary file needs to be generated - -e, --edit_intermediary Flag to let user edit intermediary -m, --generate_manifests Generate manifests from the generated intermediary file -mdir, --manifest_dir PATH The path where manifest files needs to be diff --git a/doc/source/index.rst b/doc/source/index.rst index 924681c..b0ec2b9 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -33,4 +33,3 @@ fed to Shipyard for site deployment / updates. getting_started tugboat - yaml-editor diff --git a/doc/source/yaml-editor.rst b/doc/source/yaml-editor.rst deleted file mode 100644 index 7e5a246..0000000 --- a/doc/source/yaml-editor.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. - Copyright 2018 AT&T Intellectual Property. - 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. - -.. _yaml-editor-info: - -=========== -Yaml Editor -=========== - -What is Yaml Editor? --------------------- - -Yaml Editor is a spyglass utility which lets user edit their generated -intermediary file in a browser window. It is a minimal flask app which is -invoked from the parser engine in order to let user edit fields which -could not be fetched via :ref:`tugboatinfo` plugin. - - -Yaml Editor Utility Inputs --------------------------- - - a) Yaml File: Yaml file required to be edited (This is required field) - b) Port: Port on which app shall be running - c) Host: This is only used to form URL which can be followed to open file in browser - d) String: String which is required to be updated in the file (default is '#CHANGE_ME') - -Yaml Editor Utility Usage -------------------------- - - With Spyglass (edit option is True by default): - :: - - spyglass -mg --edit_intermediary -t tugboat -x SiteDesignSpec_v0.1.xlsx -e excel_spec_upstream.yaml -d site_config.yaml -s airship-seaworthy --template_dir= - - Help: - :: - - > yaml-editor --help - Usage: yaml-editor [OPTIONS] - - Options: - -f, --file FILENAME Path with file name to the intermediary yaml file. - [required] - -h, --host TEXT Optional host parameter to run Flask on. - -p, --port INTEGER Optional port parameter to run Flask on. - -s, --string TEXT Text which is required to be changed on yaml file. - --help Show this message and exit. diff --git a/setup.py b/setup.py index 4b43c75..12326cd 100644 --- a/setup.py +++ b/setup.py @@ -30,13 +30,10 @@ setup( 'netaddr', 'pyyaml', 'jinja2', - 'flask', - 'flask-bootstrap', ], entry_points={ 'console_scripts': [ 'spyglass=spyglass.spyglass:main', - 'yaml-editor=spyglass.utils.editor.editor:main', ], 'data_extractor_plugins': ['formation=spyglass.data_extractor.plugins.formation:FormationPlugin', diff --git a/spyglass/parser/engine.py b/spyglass/parser/engine.py index f3ba441..95ab944 100644 --- a/spyglass/parser/engine.py +++ b/spyglass/parser/engine.py @@ -15,11 +15,9 @@ import copy import json import logging -import os import pkg_resources import pprint import sys -import tempfile import jsonschema import netaddr @@ -394,28 +392,11 @@ class ProcessDataSource: f.write(yaml_file) f.close() - def generate_intermediary_yaml(self, edit_intermediary=False): + def generate_intermediary_yaml(self): """ Generating intermediary yaml """ LOG.info("Start: Generate Intermediary") self._apply_design_rules() self._get_genesis_node_details() # This will validate the extracted data from different sources. self._validate_intermediary_data(self.data) - if edit_intermediary: - self.edit_intermediary_yaml() - # This will check if user edited changes are in order. - self._validate_intermediary_data(self.data) - self.intermediary_yaml = self.data - return self.intermediary_yaml - - def edit_intermediary_yaml(self): - """ Edit generated data using on browser """ - LOG.info( - "edit_intermediary_yaml: Invoking web server for yaml editing" - ) - with tempfile.NamedTemporaryFile(mode="r+") as file_obj: - yaml.safe_dump(self.data, file_obj, default_flow_style=False) - host = self._get_genesis_node_ip() - os.system("yaml-editor -f {0} -h {1}".format(file_obj.name, host)) - file_obj.seek(0) - self.data = yaml.safe_load(file_obj) + return self.data diff --git a/spyglass/spyglass.py b/spyglass/spyglass.py index 9d53a3a..7a3733e 100644 --- a/spyglass/spyglass.py +++ b/spyglass/spyglass.py @@ -64,12 +64,6 @@ LOG = logging.getLogger("spyglass") type=click.Path(exists=True), help="The path where intermediary file needs to be generated", ) -@click.option( - "--edit_intermediary/--no_edit_intermediary", - "-e/-nedit", - default=True, - help="Flag to let user edit intermediary", -) @click.option( "--generate_manifests", "-m", @@ -117,7 +111,6 @@ def main(*args, **kwargs): # Extract user provided inputs generate_intermediary = kwargs["generate_intermediary"] intermediary_dir = kwargs["intermediary_dir"] - edit_intermediary = kwargs["edit_intermediary"] generate_manifests = kwargs["generate_manifests"] manifest_dir = kwargs["manifest_dir"] intermediary = kwargs["intermediary"] @@ -208,9 +201,7 @@ def main(*args, **kwargs): ) LOG.info("Generate intermediary yaml") - intermediary_yaml = process_input_ob.generate_intermediary_yaml( - edit_intermediary - ) + intermediary_yaml = process_input_ob.generate_intermediary_yaml() else: LOG.info("Loading intermediary from user provided input") with open(intermediary, "r") as intermediary_file: diff --git a/spyglass/utils/editor/__init__.py b/spyglass/utils/editor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/spyglass/utils/editor/editor.py b/spyglass/utils/editor/editor.py deleted file mode 100644 index 726e0e3..0000000 --- a/spyglass/utils/editor/editor.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2018 AT&T Intellectual Property. All other 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. - - -import json -import logging -import os -import sys - -import click -import yaml - -from flask import Flask, request, render_template, send_from_directory -from flask_bootstrap import Bootstrap - - -app_path = os.path.dirname(os.path.abspath(__file__)) -app = Flask( - "Yaml Editor!", - template_folder=os.path.join(app_path, "templates"), - static_folder=os.path.join(app_path, "static"), -) -Bootstrap(app) -logging.getLogger("werkzeug").setLevel(logging.ERROR) -LOG = app.logger - - -@app.route("/favicon.ico") -def favicon(): - return send_from_directory(app.static_folder, "favicon.ico") - - -@app.route("/", methods=["GET", "POST"]) -def index(): - """Renders index page to edit provided yaml file.""" - LOG.info("Rendering yaml file for editing") - with open(app.config["YAML_FILE"]) as file_obj: - data = yaml.safe_load(file_obj) - return render_template( - "yaml.html", - data=json.dumps(data), - change_str=app.config["STRING_TO_CHANGE"], - ) - - -@app.route("/save", methods=["POST"]) -def save(): - """Save current progress on file.""" - LOG.info("Saving edited inputs from user to yaml file") - out = request.json.get("yaml_data") - with open(app.config["YAML_FILE"], "w") as file_obj: - yaml.safe_dump(out, file_obj, default_flow_style=False) - return "Data saved successfully!" - - -@app.route("/saveExit", methods=["POST"]) -def save_exit(): - """Save current progress on file and shuts down the server.""" - LOG.info( - "Saving edited inputs from user to yaml file and shutting" - " down server" - ) - out = request.json.get("yaml_data") - with open(app.config["YAML_FILE"], "w") as file_obj: - yaml.safe_dump(out, file_obj, default_flow_style=False) - func = request.environ.get("werkzeug.server.shutdown") - if func: - func() - return "Saved successfully, Shutting down app! You may close the tab!" - - -@app.errorhandler(404) -def page_not_found(e): - """Serves 404 error.""" - LOG.info("User tried to access unavailable page.") - return "

404: Page not Found!

" - - -def run(*args, **kwargs): - """Starts the server.""" - LOG.info("Initiating web server for yaml editing") - port = kwargs.get("port", None) - if not port: - port = 8161 - app.run(host="0.0.0.0", port=port, debug=False) - - -@click.command() -@click.option( - "--file", - "-f", - required=True, - type=click.File(), - multiple=False, - help="Path with file name to the intermediary yaml file.", -) -@click.option( - "--host", - "-h", - default="0.0.0.0", - type=click.STRING, - multiple=False, - help="Optional host parameter to run Flask on.", -) -@click.option( - "--port", - "-p", - default=8161, - type=click.INT, - multiple=False, - help="Optional port parameter to run Flask on.", -) -@click.option( - "--string", - "-s", - default="#CHANGE_ME", - type=click.STRING, - multiple=False, - help="Text which is required to be changed on yaml file.", -) -def main(*args, **kwargs): - LOG.setLevel(logging.INFO) - LOG.info("Initiating yaml-editor") - try: - yaml.safe_load(kwargs["file"]) - except yaml.YAMLError as e: - LOG.error("EXITTING - Please provide a valid yaml file.") - if hasattr(e, "problem_mark"): - mark = e.problem_mark - LOG.error( - "Error position: ({0}:{1})".format( - mark.line + 1, mark.column + 1 - ) - ) - sys.exit(2) - except Exception: - LOG.error("EXITTING - Please provide a valid yaml file.") - sys.exit(2) - LOG.info( - """ - -############################################################################## - -Please go to http://{0}:{1}/ to edit your yaml file. - -############################################################################## - - """.format( - kwargs["host"], kwargs["port"] - ) - ) - app.config["YAML_FILE"] = kwargs["file"].name - app.config["STRING_TO_CHANGE"] = kwargs["string"] - run(*args, **kwargs) - - -if __name__ == "__main__": - """Invoked when used as a script.""" - main() diff --git a/spyglass/utils/editor/static/app.js b/spyglass/utils/editor/static/app.js deleted file mode 100644 index b0e834a..0000000 --- a/spyglass/utils/editor/static/app.js +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018 AT&T Intellectual Property. All other 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. - - -// This file includes all the frond-end functionality being used for the -// yaml editor application. - - -/** - * Calls /save URL to save edit progress. - * @param {String} data Stringified JSON data. - */ -function save(data) { - $.ajax({ - type: 'POST', - url: '/save', - data: data, - success: function(res) { - setTimeout(function() { alert(res); }, 3); - }, - contentType: 'application/json;charset=UTF-8' - }); -} - -/** - * Calls /saveExit URL to save edit progress and shut down web server. - * @param {String} data Stringified JSON data. - */ -function saveAndExit(data) { - $.ajax({ - type: 'POST', - url: '/saveExit', - data: data, - success: function(res) { - setTimeout(function() { alert(res); }, 3); - }, - contentType: 'application/json;charset=UTF-8' - }); -} - -/** - * Collects and validates data from textarea. - * @returns {String} Stringified JSON data. - */ -function getSimpleData() { - var data = $("#yaml_data").val(); - try { - var index = data.indexOf(changeStr) - if (index != -1) { - var lineNum = data.substring(0, index).split('\n').length; - alert('Please change value on line '+ lineNum + '!') - return null - } - data = jsyaml.load(data) - } - catch(err) { - alert(err) - return null - } - return JSON.stringify({yaml_data : data}) -} - -/** - * Function to save edit progress. - */ -function saveSimple() { - var data = getSimpleData() - if (data) { - save(data) - } -} - -/** - * Function to save edit progress and shut down web server. - */ -function saveExitSimple() { - var data = getSimpleData() - if (data) { - saveAndExit(data) - } -} diff --git a/spyglass/utils/editor/static/favicon.ico b/spyglass/utils/editor/static/favicon.ico deleted file mode 100644 index d3dca27a02a14c2554c5ae693af87100e1d5359f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmb7?TS!xJ9LN9ZqM#`0A*7djtf!!tpeM1H=qaM7vLJ~POGcTOAkBr9SjI}`=G?_i zk8bD8UEEEVx4Frh#&pwkn_8x(cGJA9h>CCjbEMG1C=Q?h@BjZDKEMB!kWKVUNg=e| zN3ukO>?VZlr_dD5tmbJTg#PpGSVb8BM^kgLoYb7ZzFC!{xuLr1SyGFc22FO!o_{H! zIa@-c>0%LEAQc;n4U5YB2`KYo3BCsJYqIYX`HAa9m07x7d8Q~;d3p^Q#i^>ZC8;_| zt=L%OT=dm)u-tP&ng0^1t1Sz>uf0-1PSR~MlG?H+jc44fYmfhgj^oT-X4K#gCz$9m z>Y94LchefiqQ-gE742kP!XUV-{(ccF_??13{NgO<%)qU7!Nat{=js#IF&Lc%cUPZl zud;6Fk4(&?FAzh}Ka4;}1kZZM5FVOF|G% zO?(vQ|BLTx^ddI7fU&oq){wv$y^Ha8pAn{Y*-~9@b7j-I`T6s+FkU#Uy=XB9&}t2? z#}*XM#wz9d>+@x4jW?C+eD<+LIDdz;9}agI4%ctcoYmm}rYn$N;_ey9g~P|z_y" + lineNo + ""); - else - codeLines.append("
" + lineNo + "
"); - - lineNo++; - } - return lineNo; - }; - - - /* - * Iterate through each of the elements are to be applied to - */ - return this.each(function() { - var lineNo = 1; - var textarea = $(this); - - /* Turn off the wrapping of as we don't want to screw up the line numbers */ - textarea.attr("wrap", "off"); - textarea.css({resize:'none'}); - var originalTextAreaWidth = textarea.outerWidth(); - - /* Wrap the text area in the elements we need */ - textarea.wrap("
"); - var linedTextAreaDiv = textarea.parent().wrap("
"); - var linedWrapDiv = linedTextAreaDiv.parent(); - - linedWrapDiv.prepend("
"); - - var linesDiv = linedWrapDiv.find(".lines"); - linesDiv.height( textarea.height() + 6 ); - - - /* Draw the number bar; filling it out where necessary */ - linesDiv.append( "
" ); - var codeLinesDiv = linesDiv.find(".codelines"); - lineNo = fillOutLines( codeLinesDiv, linesDiv.height(), 1 ); - - /* Move the textarea to the selected line */ - if ( opts.selectedLine != -1 && !isNaN(opts.selectedLine) ){ - var fontSize = parseInt( textarea.height() / (lineNo-2) ); - var position = parseInt( fontSize * opts.selectedLine ) - (textarea.height()/2); - textarea[0].scrollTop = position; - } - - - /* Set the width */ - var sidebarWidth = linesDiv.outerWidth(); - var paddingHorizontal = parseInt( linedWrapDiv.css("border-left-width") ) + parseInt( linedWrapDiv.css("border-right-width") ) + parseInt( linedWrapDiv.css("padding-left") ) + parseInt( linedWrapDiv.css("padding-right") ); - var linedWrapDivNewWidth = originalTextAreaWidth - paddingHorizontal; - var textareaNewWidth = originalTextAreaWidth - sidebarWidth - paddingHorizontal - 20; - - textarea.width( textareaNewWidth ); - linedWrapDiv.width( linedWrapDivNewWidth ); - - - - /* React to the scroll event */ - textarea.scroll( function(tn){ - var domTextArea = $(this)[0]; - var scrollTop = domTextArea.scrollTop; - var clientHeight = domTextArea.clientHeight; - codeLinesDiv.css( {'margin-top': (-1*scrollTop) + "px"} ); - lineNo = fillOutLines( codeLinesDiv, scrollTop + clientHeight, lineNo ); - }); - - - /* Should the textarea get resized outside of our control */ - textarea.resize( function(tn){ - var domTextArea = $(this)[0]; - linesDiv.height( domTextArea.clientHeight + 6 ); - }); - - }); - }; - - // default options - $.fn.linedtextarea.defaults = { - selectedLine: -1, - selectedClass: 'lineselect' - }; -})(jQuery); diff --git a/spyglass/utils/editor/static/js-yaml.min.js b/spyglass/utils/editor/static/js-yaml.min.js deleted file mode 100644 index 078bf73..0000000 --- a/spyglass/utils/editor/static/js-yaml.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).jsyaml=e()}}(function(){return function o(a,s,c){function u(t,e){if(!s[t]){if(!a[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);var i=new Error("Cannot find module '"+t+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[t]={exports:{}};a[t][0].call(r.exports,function(e){return u(a[t][1][e]||e)},r,r.exports,o,a,s,c)}return s[t].exports}for(var l="function"==typeof require&&require,e=0;e=i.flowLevel;switch(H(r,n,i.indent,t,function(e){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n"+V(r,i.indent)+Z(L(function(e,t){var n,i,r=/(\n+)([^\n]*)/g,o=(s=e.indexOf("\n"),s=-1!==s?s:e.length,r.lastIndex=s,z(e.slice(0,s),t)),a="\n"===e[0]||" "===e[0];var s;for(;i=r.exec(e);){var c=i[1],u=i[2];n=" "===u[0],o+=c+(a||n||""===u?"":"\n")+z(u,t),a=n}return o}(r,t),e));case $:return'"'+function(e){for(var t,n,i,r="",o=0;ot&&o tag resolver accepts not "'+c+'" style');i=s.represent[c](t,c)}e.dump=i}return!0}return!1}function Q(e,t,n,i,r,o){e.tag=null,e.dump=n,J(e,n,!1)||J(e,n,!0);var a=l.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(c=-1!==(s=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||c||2!==e.indent&&0 "+e.dump)}return!0}function X(e,t){var n,i,r=[],o=[];for(function e(t,n,i){var r,o,a;if(null!==t&&"object"==typeof t)if(-1!==(o=n.indexOf(t)))-1===i.indexOf(o)&&i.push(o);else if(n.push(t),Array.isArray(t))for(o=0,a=t.length;ot)&&0!==i)_(e,"bad indentation of a sequence entry");else if(e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt)&&(K(e,t,b,!0,r)&&(m?d=e.result:h=e.result),m||(D(e,l,p,f,d,h,o,a),f=d=h=null),q(e,!0,-1),s=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==s)_(e,"bad indentation of a mapping entry");else if(e.lineIndentl&&(l=e.lineIndent),j(o))p++;else{if(e.lineIndent>10),56320+(c-65536&1023)),e.position++}else _(e,"unknown escape sequence");n=i=e.position}else j(s)?(T(e,n,i,!0),R(e,q(e,!1,t)),n=i=e.position):e.position===e.lineStart&&Y(e)?_(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}_(e,"unexpected end of the stream within a double quoted scalar")}(e,p)?m=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!I(i)&&!E(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&_(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||_(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],q(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,s,c,u,l,p=e.kind,f=e.result;if(I(l=e.input.charCodeAt(e.position))||E(l)||35===l||38===l||42===l||33===l||124===l||62===l||39===l||34===l||37===l||64===l||96===l)return!1;if((63===l||45===l)&&(I(i=e.input.charCodeAt(e.position+1))||n&&E(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==l;){if(58===l){if(I(i=e.input.charCodeAt(e.position+1))||n&&E(i))break}else if(35===l){if(I(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&Y(e)||n&&E(l))break;if(j(l)){if(s=e.line,c=e.lineStart,u=e.lineIndent,q(e,!1,-1),e.lineIndent>=t){a=!0,l=e.input.charCodeAt(e.position);continue}e.position=o,e.line=s,e.lineStart=c,e.lineIndent=u;break}}a&&(T(e,r,o,!1),R(e,e.line-s),r=o=e.position,a=!1),S(l)||(o=e.position+1),l=e.input.charCodeAt(++e.position)}return T(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,p,x===n)&&(m=!0,null===e.tag&&(e.tag="?")):(m=!0,null===e.tag&&null===e.anchor||_(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===d&&(m=s&&B(e,f))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(c=0,u=e.implicitTypes.length;c tag; it should be "'+l.kind+'", not "'+e.kind+'"'),l.resolve(e.result)?(e.result=l.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):_(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):_(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||m}function $(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(r=e.input.charCodeAt(e.position))&&(q(e,!0,-1),r=e.input.charCodeAt(e.position),!(0t/2-1){n=" ... ",i+=5;break}for(r="",o=this.position;ot/2-1){r=" ... ",o-=5;break}return a=this.buffer.slice(i,o),s.repeat(" ",e)+n+a+r+"\n"+s.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet())&&(n+=":\n"+t),n},t.exports=i},{"./common":2}],7:[function(e,t,n){"use strict";var i=e("./common"),r=e("./exception"),o=e("./type");function a(e,t,i){var r=[];return e.include.forEach(function(e){i=a(e,t,i)}),e[t].forEach(function(n){i.forEach(function(e,t){e.tag===n.tag&&e.kind===n.kind&&r.push(t)}),i.push(n)}),i.filter(function(e,t){return-1===r.indexOf(t)})}function s(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new r("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=a(this,"implicit",[]),this.compiledExplicit=a(this,"explicit",[]),this.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{}};function i(e){n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e>16&255),s.push(a>>8&255),s.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0==(n=r%4*6)?(s.push(a>>16&255),s.push(a>>8&255),s.push(255&a)):18===n?(s.push(a>>10&255),s.push(a>>2&255)):12===n&&s.push(a>>4&255),c?c.from?c.from(s):new c(s):s},predicate:function(e){return c&&c.isBuffer(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=u;for(t=0;t>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0==(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}})},{"../type":13}],15:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type"),o=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var a=/^[-+]?[0-9]+e/;t.exports=new r("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!o.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n,i,r;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,r=[],0<="+-".indexOf(t[0])&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:0<=t.indexOf(":")?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||i.isNegativeZero(e))},represent:function(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(i.isNegativeZero(e))return"-0.0";return n=e.toString(10),a.test(n)?n.replace("e",".e"):n},defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type");t.exports=new r("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i,r,o=e.length,a=0,s=!1;if(!o)return!1;if("-"!==(t=e[a])&&"+"!==t||(t=e[++a]),"0"===t){if(a+1===o)return!0;if("b"===(t=e[++a])){for(a++;a -{% endblock %} - -{% block scripts %} -{{super()}} - - - - -{% endblock %} - -{% block content %} - -
-
-
Edit your YAML (Update corresponding fields with {{ change_str }} text):
-
- -
-
-
- - -
-
- -{% endblock %}