|
|
|
@@ -0,0 +1,542 @@ |
|
|
|
class Api::V1::Projects::PipelinesController < Api::V1::BaseController |
|
|
|
before_action :require_manager_above |
|
|
|
|
|
|
|
def index |
|
|
|
@pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc) |
|
|
|
@pipelines = paginate @pipelines |
|
|
|
end |
|
|
|
|
|
|
|
def create |
|
|
|
size = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id).size |
|
|
|
tip_exception("已经存在#{params[:pipeline_name]}流水线!") if size > 0 |
|
|
|
@pipeline = Action::Pipeline.new(pipeline_name: params[:pipeline_name], project_id: @project.id) |
|
|
|
@pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yaml" |
|
|
|
@pipeline.json = demo.to_json |
|
|
|
# @pipeline.json = params[:json] if params[:json] |
|
|
|
@pipeline.yaml = build_pipeline_yaml(@pipeline) |
|
|
|
@pipeline.save! |
|
|
|
sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch) |
|
|
|
tip_exception("#{@pipeline.file_name}已存在") if sha |
|
|
|
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params) |
|
|
|
tip_exception(interactor.error) unless interactor.success? |
|
|
|
render_ok({ id: @pipeline.id }) |
|
|
|
end |
|
|
|
|
|
|
|
def update |
|
|
|
@pipeline = Action::Pipeline.find(params[:id]) |
|
|
|
@pipeline.update!(pipeline_name: params[:pipeline_name]) |
|
|
|
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params) |
|
|
|
tip_exception(interactor.error) unless interactor.success? |
|
|
|
render_ok |
|
|
|
end |
|
|
|
|
|
|
|
def destroy |
|
|
|
@pipeline = Action::Pipeline.find(params[:id]) |
|
|
|
if pipeline |
|
|
|
interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, content_params) |
|
|
|
tip_exception(interactor.error) unless interactor.success? |
|
|
|
@pipeline.destroy! |
|
|
|
end |
|
|
|
render_ok |
|
|
|
end |
|
|
|
|
|
|
|
def show |
|
|
|
@pipeline = Action::Pipeline.find_by(id: params[:id]) |
|
|
|
@pipeline = Action::Pipeline.new(id: 0, pipeline_name: "test-ss", yaml: build_yaml) if @pipeline.blank? |
|
|
|
end |
|
|
|
|
|
|
|
def build_pipeline_yaml(pipeline) |
|
|
|
if pipeline.json.present? |
|
|
|
@name = pipeline.pipeline_name |
|
|
|
params_nodes = JSON.parse(pipeline.json)["nodes"].select { |node| !["on-push", "on-schedule"].include?(node["component_name"]) } |
|
|
|
on_nodes = JSON.parse(pipeline.json)["nodes"].select { |node| ["on-push", "on-schedule"].include?(node["component_name"]) } |
|
|
|
@on_nodes = build_nodes(on_nodes) |
|
|
|
@steps_nodes = build_nodes(params_nodes) |
|
|
|
yaml = ERB.new(File.read(File.join(Rails.root, "app/views/api/v1/projects/pipelines", "build_pipeline.yaml.erb"))).result(binding) |
|
|
|
# 删除空行内容 |
|
|
|
@pipeline_yaml = yaml.gsub(/^\s*\n/, "") |
|
|
|
else |
|
|
|
@pipeline_yaml = params[:yaml] |
|
|
|
end |
|
|
|
@pipeline_yaml |
|
|
|
end |
|
|
|
|
|
|
|
def build_yaml |
|
|
|
@name = "love me" |
|
|
|
params_nodes = JSON.parse(demo.to_json)["nodes"].select { |node| !["on-push", "on-schedule"].include?(node["component_name"]) } |
|
|
|
on_nodes = JSON.parse(demo.to_json)["nodes"].select { |node| ["on-push", "on-schedule"].include?(node["component_name"]) } |
|
|
|
@on_nodes = build_nodes(on_nodes) |
|
|
|
@steps_nodes = [] |
|
|
|
params_nodes.each do |input_node| |
|
|
|
# Rails.logger.info "input_node=====0===#{input_node["component_name"]}======#{input_node["in_parameters"]}" |
|
|
|
node = Action::Node.find_by(name: input_node["component_name"]) |
|
|
|
node.cust_name = input_node["component_label"] if input_node["component_label"].present? |
|
|
|
input_values = {} |
|
|
|
if input_node["in_parameters"].present? |
|
|
|
# Rails.logger.info "@in_parameters=====11===#{input_node["component_name"]}======#{input_node["in_parameters"]}" |
|
|
|
input_node["in_parameters"].each_key do |input_key| |
|
|
|
# Rails.logger.info "@in_parameters.input_key===#{input_key}" |
|
|
|
# Rails.logger.info "@in_parameters.input_value===#{input_node["in_parameters"][input_key]["value"]}" |
|
|
|
input_values = input_values.merge({ "#{input_key.gsub("--", "")}": "#{input_node["in_parameters"][input_key]["value"]}" }) |
|
|
|
end |
|
|
|
node.input_values = input_values |
|
|
|
# Rails.logger.info "@input_values node===#{node.input_values.to_json}" |
|
|
|
end |
|
|
|
@steps_nodes.push(node) |
|
|
|
end |
|
|
|
Rails.logger.info "@@on_nodes===#{@on_nodes.to_json}" |
|
|
|
Rails.logger.info "@steps_nodes===#{@steps_nodes.to_json}" |
|
|
|
yaml = ERB.new(File.read(File.join(Rails.root, "app/views/api/v1/projects/pipelines", "build_pipeline.yaml.erb"))).result(binding) |
|
|
|
@pipeline_yaml = yaml.gsub(/^\s*\n/, "") |
|
|
|
Rails.logger.info "=========================" |
|
|
|
Rails.logger.info @pipeline_yaml |
|
|
|
@pipeline_yaml |
|
|
|
end |
|
|
|
|
|
|
|
private |
|
|
|
|
|
|
|
def get_pipeline_file_sha(file_name, branch) |
|
|
|
file_path_uri = URI.parse(file_name) |
|
|
|
interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: branch || 'master') |
|
|
|
if interactor.success? |
|
|
|
file = interactor.result |
|
|
|
file['sha'] |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
def content_params |
|
|
|
{ |
|
|
|
filepath: ".gitea/workflows/#{@pipeline.pipeline_name}.yaml", |
|
|
|
branch: @pipeline.branch, |
|
|
|
new_branch: @pipeline.branch, |
|
|
|
content: build_pipeline_yaml(@pipeline), |
|
|
|
message: 'create pipeline', |
|
|
|
committer: { |
|
|
|
email: current_user.mail, |
|
|
|
name: current_user.login |
|
|
|
}, |
|
|
|
identifier: @project.identifier |
|
|
|
} |
|
|
|
end |
|
|
|
|
|
|
|
def build_nodes(params_nodes) |
|
|
|
steps_nodes = [] |
|
|
|
params_nodes.each do |input_node| |
|
|
|
node = Action::Node.find_by(name: input_node["component_name"]) |
|
|
|
node.cust_name = input_node["component_label"] if input_node["component_label"].present? |
|
|
|
input_values = {} |
|
|
|
if input_node["in_parameters"].present? |
|
|
|
# Rails.logger.info "@in_parameters=====11===#{input_node["component_name"]}======#{input_node["in_parameters"]}" |
|
|
|
input_node["in_parameters"].each_key do |input_key| |
|
|
|
# Rails.logger.info "@in_parameters.input_key===#{input_key}" |
|
|
|
# Rails.logger.info "@in_parameters.input_value===#{input_node["in_parameters"][input_key]["value"]}" |
|
|
|
input_values = input_values.merge({ "#{input_key.gsub("--", "")}": "#{input_node["in_parameters"][input_key]["value"]}" }) |
|
|
|
end |
|
|
|
# Rails.logger.info "@input_values node1===#{input_values}" |
|
|
|
node.input_values = input_values |
|
|
|
# Rails.logger.info "@input_values node===#{node.input_values.to_json}" |
|
|
|
end |
|
|
|
steps_nodes.push(node) |
|
|
|
end |
|
|
|
steps_nodes |
|
|
|
end |
|
|
|
|
|
|
|
def demo |
|
|
|
{ |
|
|
|
"nodes": [ |
|
|
|
{ |
|
|
|
"id": "git-clone-245734ab", |
|
|
|
"category_id": 1, |
|
|
|
"component_name": "on-schedule", |
|
|
|
"component_label": "触发器", |
|
|
|
"working_directory": "", |
|
|
|
"command": "", |
|
|
|
"in_parameters": { |
|
|
|
"--cro": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "push代码", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "", |
|
|
|
"placeholder": "私有仓库填写ssh地址,公有仓库填写https git地址", |
|
|
|
"describe": "代码仓库地址", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "15 4,5 * * *" |
|
|
|
}, |
|
|
|
"--paths-ignore": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "push代码", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "", |
|
|
|
"placeholder": "私有仓库填写ssh地址,公有仓库填写https git地址", |
|
|
|
"describe": "代码仓库地址", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "**.md" |
|
|
|
} |
|
|
|
}, |
|
|
|
"out_parameters": { |
|
|
|
"--code_output": { |
|
|
|
"type": "str", |
|
|
|
"label": "代码输出路径", |
|
|
|
"path": "/code", |
|
|
|
"require": 1, |
|
|
|
"value": "/code" |
|
|
|
} |
|
|
|
}, |
|
|
|
"description": "代码拉取组件", |
|
|
|
"icon_path": "component-icon-1", |
|
|
|
"create_by": "admin", |
|
|
|
"create_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"update_by": "admin", |
|
|
|
"update_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"state": 1, |
|
|
|
"image": "172.20.32.187/pipeline-component/built-in/git:202312071000", |
|
|
|
"env_variables": "", |
|
|
|
"x": 532, |
|
|
|
"y": 202, |
|
|
|
"label": "代码拉取", |
|
|
|
"img": "/assets/images/component-icon-1.png", |
|
|
|
"isCluster": false, |
|
|
|
"type": "rect-node", |
|
|
|
"size": [110, 36], |
|
|
|
"--code_path": "https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git", |
|
|
|
"--branch": "train_ci_test", |
|
|
|
"--depth": "1", |
|
|
|
"--code_output": "/code" |
|
|
|
}, |
|
|
|
{ |
|
|
|
"id": "git-clone-245734ab", |
|
|
|
"category_id": 1, |
|
|
|
"component_name": "git-clone", |
|
|
|
"component_label": "代码拉取", |
|
|
|
"working_directory": "", |
|
|
|
"command": "", |
|
|
|
"in_parameters": { |
|
|
|
|
|
|
|
}, |
|
|
|
"out_parameters": { |
|
|
|
"--code_output": { |
|
|
|
"type": "str", |
|
|
|
"label": "代码输出路径", |
|
|
|
"path": "/code", |
|
|
|
"require": 1, |
|
|
|
"value": "/code" |
|
|
|
} |
|
|
|
}, |
|
|
|
"description": "代码拉取组件", |
|
|
|
"icon_path": "component-icon-1", |
|
|
|
"create_by": "admin", |
|
|
|
"create_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"update_by": "admin", |
|
|
|
"update_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"state": 1, |
|
|
|
"image": "172.20.32.187/pipeline-component/built-in/git:202312071000", |
|
|
|
"env_variables": "", |
|
|
|
"x": 532, |
|
|
|
"y": 202, |
|
|
|
"label": "代码拉取", |
|
|
|
"img": "/assets/images/component-icon-1.png", |
|
|
|
"isCluster": false, |
|
|
|
"type": "rect-node", |
|
|
|
"size": [110, 36], |
|
|
|
"--code_path": "https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git", |
|
|
|
"--branch": "train_ci_test", |
|
|
|
"--depth": "1", |
|
|
|
"--code_output": "/code" |
|
|
|
}, |
|
|
|
{ |
|
|
|
"id": "git-clone-245734ab", |
|
|
|
"category_id": 1, |
|
|
|
"component_name": "setup-java", |
|
|
|
"component_label": "安装java环境", |
|
|
|
"working_directory": "", |
|
|
|
"command": "", |
|
|
|
"in_parameters": { |
|
|
|
"--distribution": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "代码仓库地址", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "", |
|
|
|
"placeholder": "私有仓库填写ssh地址,公有仓库填写https git地址", |
|
|
|
"describe": "代码仓库地址", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "jdkfile" |
|
|
|
}, |
|
|
|
"--java-version": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "代码分支/tag", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "master", |
|
|
|
"placeholder": "", |
|
|
|
"describe": "代码分支或者tag", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "11.0.0" |
|
|
|
}, |
|
|
|
"--architecture": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "克隆深度", |
|
|
|
"require": 0, |
|
|
|
"choice": [], |
|
|
|
"default": "1", |
|
|
|
"placeholder": "", |
|
|
|
"describe": "代码克隆深度", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "x64" |
|
|
|
}, |
|
|
|
"--mvn-toolchain-vendor": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "ssh私钥", |
|
|
|
"require": 0, |
|
|
|
"choice": [], |
|
|
|
"default": "1", |
|
|
|
"placeholder": "", |
|
|
|
"describe": "ssh私钥,确保ssh公钥已经托管到代码平台,否则可能拉取失败", |
|
|
|
"editable": 1, |
|
|
|
"value": "Oracle" |
|
|
|
} |
|
|
|
}, |
|
|
|
"out_parameters": { |
|
|
|
"--code_output": { |
|
|
|
"type": "str", |
|
|
|
"label": "代码输出路径", |
|
|
|
"path": "/code", |
|
|
|
"require": 1, |
|
|
|
"value": "/code" |
|
|
|
} |
|
|
|
}, |
|
|
|
"description": "代码拉取组件", |
|
|
|
"icon_path": "component-icon-1", |
|
|
|
"create_by": "admin", |
|
|
|
"create_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"update_by": "admin", |
|
|
|
"update_time": "2024-03-02T05:41:25.000+00:00", |
|
|
|
"state": 1, |
|
|
|
"image": "172.20.32.187/pipeline-component/built-in/git:202312071000", |
|
|
|
"env_variables": "", |
|
|
|
"x": 532, |
|
|
|
"y": 202, |
|
|
|
"label": "代码拉取", |
|
|
|
"img": "/assets/images/component-icon-1.png", |
|
|
|
"isCluster": false, |
|
|
|
"type": "rect-node", |
|
|
|
"size": [110, 36], |
|
|
|
"--code_path": "https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git", |
|
|
|
"--branch": "train_ci_test", |
|
|
|
"--depth": "1", |
|
|
|
"--code_output": "/code" |
|
|
|
}, |
|
|
|
{ |
|
|
|
"id": "git-clone-245734ab", |
|
|
|
"category_id": 1, |
|
|
|
"component_name": "shell", |
|
|
|
"component_label": "执行shell命令", |
|
|
|
"working_directory": "", |
|
|
|
"command": "", |
|
|
|
"in_parameters": { |
|
|
|
"--run": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "代码仓库地址", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "", |
|
|
|
"placeholder": "私有仓库填写ssh地址,公有仓库填写https git地址", |
|
|
|
"describe": "代码仓库地址", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "service nginx restart" |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
"id": "git-clone-245734ab", |
|
|
|
"category_id": 1, |
|
|
|
"component_name": "scp", |
|
|
|
"component_label": "scp", |
|
|
|
"working_directory": "", |
|
|
|
"command": "", |
|
|
|
"in_parameters": { |
|
|
|
"--run": { |
|
|
|
"type": "str", |
|
|
|
"item_type": "", |
|
|
|
"label": "代码仓库地址", |
|
|
|
"require": 1, |
|
|
|
"choice": [], |
|
|
|
"default": "", |
|
|
|
"placeholder": "私有仓库填写ssh地址,公有仓库填写https git地址", |
|
|
|
"describe": "代码仓库地址", |
|
|
|
"editable": 1, |
|
|
|
"condition": "", |
|
|
|
"value": "service nginx restart" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
], |
|
|
|
# "edges": [ |
|
|
|
# { |
|
|
|
# "source": "git-clone-245734ab", |
|
|
|
# "target": "model-train-09b1491", |
|
|
|
# "style": { |
|
|
|
# "active": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "selected": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 2, |
|
|
|
# "shadowColor": "rgb(95, 149, 255)", |
|
|
|
# "shadowBlur": 10, |
|
|
|
# "text-shape": { |
|
|
|
# "fontWeight": 500 |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "highlight": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 2, |
|
|
|
# "text-shape": { |
|
|
|
# "fontWeight": 500 |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "inactive": { |
|
|
|
# "stroke": "rgb(234, 234, 234)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "disable": { |
|
|
|
# "stroke": "rgb(245, 245, 245)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "endArrow": { |
|
|
|
# "path": "M 6,0 L 9,-1.5 L 9,1.5 Z", |
|
|
|
# "d": 4.5, |
|
|
|
# "fill": "#CDD0DC" |
|
|
|
# }, |
|
|
|
# "cursor": "pointer", |
|
|
|
# "lineWidth": 1, |
|
|
|
# "opacity": 1, |
|
|
|
# "stroke": "#CDD0DC", |
|
|
|
# "radius": 1 |
|
|
|
# }, |
|
|
|
# "nodeStateStyle": { |
|
|
|
# "hover": { |
|
|
|
# "opacity": 1, |
|
|
|
# "stroke": "#8fe8ff" |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "labelCfg": { |
|
|
|
# "autoRotate": true, |
|
|
|
# "style": { |
|
|
|
# "fontSize": 10, |
|
|
|
# "fill": "#FFF" |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "id": "edge-0.11773197923997381714446043619", |
|
|
|
# "startPoint": { |
|
|
|
# "x": 532, |
|
|
|
# "y": 220.25, |
|
|
|
# "anchorIndex": 1 |
|
|
|
# }, |
|
|
|
# "endPoint": { |
|
|
|
# "x": 530, |
|
|
|
# "y": 304.75, |
|
|
|
# "anchorIndex": 0 |
|
|
|
# }, |
|
|
|
# "targetAnchor": 0, |
|
|
|
# "type": "cubic-vertical", |
|
|
|
# "curveOffset": [0, 0], |
|
|
|
# "curvePosition": [0.5, 0.5], |
|
|
|
# "minCurveOffset": [0, 0], |
|
|
|
# "depth": 0 |
|
|
|
# }, |
|
|
|
# { |
|
|
|
# "source": "model-train-09b1491", |
|
|
|
# "target": "model-evaluate-b401ff0", |
|
|
|
# "style": { |
|
|
|
# "active": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "selected": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 2, |
|
|
|
# "shadowColor": "rgb(95, 149, 255)", |
|
|
|
# "shadowBlur": 10, |
|
|
|
# "text-shape": { |
|
|
|
# "fontWeight": 500 |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "highlight": { |
|
|
|
# "stroke": "rgb(95, 149, 255)", |
|
|
|
# "lineWidth": 2, |
|
|
|
# "text-shape": { |
|
|
|
# "fontWeight": 500 |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "inactive": { |
|
|
|
# "stroke": "rgb(234, 234, 234)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "disable": { |
|
|
|
# "stroke": "rgb(245, 245, 245)", |
|
|
|
# "lineWidth": 1 |
|
|
|
# }, |
|
|
|
# "endArrow": { |
|
|
|
# "path": "M 6,0 L 9,-1.5 L 9,1.5 Z", |
|
|
|
# "d": 4.5, |
|
|
|
# "fill": "#CDD0DC" |
|
|
|
# }, |
|
|
|
# "cursor": "pointer", |
|
|
|
# "lineWidth": 1, |
|
|
|
# "opacity": 1, |
|
|
|
# "stroke": "#CDD0DC", |
|
|
|
# "radius": 1 |
|
|
|
# }, |
|
|
|
# "nodeStateStyle": { |
|
|
|
# "hover": { |
|
|
|
# "opacity": 1, |
|
|
|
# "stroke": "#8fe8ff" |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "labelCfg": { |
|
|
|
# "autoRotate": true, |
|
|
|
# "style": { |
|
|
|
# "fontSize": 10, |
|
|
|
# "fill": "#FFF" |
|
|
|
# } |
|
|
|
# }, |
|
|
|
# "id": "edge-0.28238605806531771714446047075", |
|
|
|
# "startPoint": { |
|
|
|
# "x": 530, |
|
|
|
# "y": 341.25, |
|
|
|
# "anchorIndex": 1 |
|
|
|
# }, |
|
|
|
# "endPoint": { |
|
|
|
# "x": 520, |
|
|
|
# "y": 431.75, |
|
|
|
# "anchorIndex": 0 |
|
|
|
# }, |
|
|
|
# "targetAnchor": 0, |
|
|
|
# "type": "cubic-vertical", |
|
|
|
# "curveOffset": [0, 0], |
|
|
|
# "curvePosition": [0.5, 0.5], |
|
|
|
# "minCurveOffset": [0, 0], |
|
|
|
# "depth": 0 |
|
|
|
# } |
|
|
|
# ] |
|
|
|
} |
|
|
|
end |
|
|
|
end |