From 495bbca83eae52537b0acfd429417a3bf16313bc Mon Sep 17 00:00:00 2001 From: Matthew Pope Date: Fri, 8 Sep 2023 11:38:32 -0700 Subject: [PATCH] Improve sandbox samples --- assets/ion-schema-widget.js | 250 ++++++++++++++++++++++++++---------- sandbox.md | 4 +- 2 files changed, 185 insertions(+), 69 deletions(-) diff --git a/assets/ion-schema-widget.js b/assets/ion-schema-widget.js index ebd9996..79f3d57 100644 --- a/assets/ion-schema-widget.js +++ b/assets/ion-schema-widget.js @@ -3,34 +3,53 @@ const validateButton = document.getElementById("validate"); const shareButton = document.getElementById("share"); const dropDownSelection = document.getElementById("examples"); -function loadPage(schemaInputValue, valueInputValue, schemaTypeInputValue) { +function initPage() { + let sampleIds = Object.keys(SAMPLES); + + // Check for data in the URL and add it to "SAMPLES" const params = new Proxy(new URLSearchParams(window.location.search), { get: (searchParams, prop) => searchParams.get(prop), }); - - // If there are any query params in the URL, then use those and skip all the default values + const SHARED_ID = "shared_in_url" if (params.schema || params.value || params.type) { - schemaInputValue = params.schema ?? ""; - valueInputValue = params.value ?? ""; - schemaTypeInputValue = params.type ?? ""; + sampleIds.unshift(SHARED_ID) // Add the "Shared in URL" to the top of the list + SAMPLES[SHARED_ID] = { + displayName: "(Shared in URL)", + schema: params.schema ?? "", + value: params.value ?? "", + type: params.type ?? "", + }; + } + + // Set up the "samples" dropdown + for (let i = 0; i < sampleIds.length; ++i) { + let id = sampleIds[i] + dropDownSelection.add(new Option(SAMPLES[id].displayName, id)); } + if (SAMPLES[SHARED_ID]) { + dropDownSelection.selectedIndex = sampleIds.indexOf(SHARED_ID) + } + dropDownSelection.onchange() +} + +function populateInputFields(sample) { + let { schema, value, type } = sample; ace.edit("schema").setOptions({ mode: 'ace/mode/ion', theme: 'ace/theme/cloud9_day', showPrintMargin: false, tabSize: 2, - value: schemaInputValue, + value: trimIndent(schema), }); ace.edit("value").setOptions({ mode: 'ace/mode/ion', theme: 'ace/theme/cloud9_day', showPrintMargin: false, tabSize: 2, - value: valueInputValue, + value: trimIndent(value), }); - document.getElementById("schema_type").value = schemaTypeInputValue; - + document.getElementById("schema_type").value = type; // clear any previous validation results const pre = document.getElementById('result'); @@ -41,14 +60,6 @@ function loadPage(schemaInputValue, valueInputValue, schemaTypeInputValue) { _set_output_style(resultDiv, "primary") } -loadPage("type::{\n" + - " name: short_string,\n" + - " type: string,\n" + - " codepoint_length: range::[1, 10],\n" + - "}", - "\"Hello World!\"", - "short_string"); - function _set_output_style(resultDiv, styleName) { var toRemove = []; resultDiv.classList.forEach(value => { @@ -146,52 +157,159 @@ const copyUrl = () => { shareButton.addEventListener("click", copyUrl); dropDownSelection.onchange = function() { - var e = document.getElementById("examples"); - var value = e.value; - if (value === "simpleTypeDefinition") { - // Default values for populating the input fields - let schemaInputValue = "type::{\n" + - " name: short_string,\n" + - " type: string,\n" + - " codepoint_length: range::[1, 10],\n" + - "}"; - let valueInputValue = "\"Hello World!\""; - let schemaTypeInputValue = "short_string" - - loadPage(schemaInputValue, valueInputValue, schemaTypeInputValue) - } else if (value === "typeDefinitionWithFields") { - // Default values for populating the input fields - let schemaInputValue = "type::{\n" + - " name: customer,\n" + - " type: struct,\n" + - " fields: {\n" + - " firstName: { type: string, occurs: required },\n" + - " middleName: string,\n" + - " lastName: { type: string, codepoint_length: range::[min, 7], occurs: required },\n" + - " age: { type: int, valid_values: range::[1, max] },\n" + - " }\n" + - "}"; - let valueInputValue = "{\n" + - " firstName: \"John\",\n" + - " lastName: \"Doe\",\n" + - " age: -5,\n" + - " }"; - let schemaTypeInputValue = "customer" - - loadPage(schemaInputValue, valueInputValue, schemaTypeInputValue) - } else if (value === "typeDefinitionWithLogicConstraints") { - // Default values for populating the input fields - let schemaInputValue = "type::{\n" + - " name: any_of_core_types,\n" + - " any_of: [\n" + - " bool,\n" + - " int,\n" + - " string,\n" + - " ],\n" + - "}"; - let valueInputValue = "hi"; - let schemaTypeInputValue = "any_of_core_types" - - loadPage(schemaInputValue, valueInputValue, schemaTypeInputValue) - } + let sample = SAMPLES[dropDownSelection.value]; + populateInputFields(sample) }; + +/** + * Detects a common minimal indent of all the input lines, removes it from every line. + * Note that blank lines do not affect the detected indent level. + */ +function trimIndent(str) { + let lines = str.split("\n").filter((l) => l.length > 0) + let indentLength = Math.min(...lines.filter((l) => l.trim().length > 0).map((l) => l.length - l.trimStart().length)) + return lines.map((l) => l.substring(indentLength)).join("\n") +} + +const SAMPLES = { + simpleTypeDefinition: { + displayName: "Simple Type Definition", + schema: ` + $ion_schema_2_0 + type::{ + name: short_string, + type: string, + codepoint_length: range::[1, 10], + } + `, + type: "short_string", + value: `"Hello World!"` + }, + typeDefinitionWithFields: { + displayName: "Type Definition with fields", + schema: ` + $ion_schema_2_0 + type::{ + name: customer, + type: struct, + fields: closed::{ + firstName: { type: string, occurs: required }, + middleName: string, + lastName: { type: string, occurs: required }, + age: { type: int, valid_values: range::[1, max], } + } + }`, + type: "customer", + value: `{ firstName: "John", lastName: "Doe", age: -5 }`, + }, + typeDefinitionWithLogicConstraints: { + displayName: "Type Definition with logic constraints", + schema: ` + $ion_schema_2_0 + type::{ + name: string_or_bool, + any_of: [string, bool], + }`, + type: "string_or_bool", + value: `hi` + }, + versionedType: { + displayName: "Versioned Type", + schema: ` + $ion_schema_2_0 + type::{ + // The 'widget' type includes all versions of widgets + name: widget, + any_of: [widget_v1, widget_v2], + } + type::{ + // The 'widget_latest' type is an alias that always points to the latest version of widget + name: widget_latest, + type: widget_v2, + } + type::{ + name: widget_v1, + fields: closed::{ + name: string, + part_id: int, + component_ids: { type: list, element: int } + } + } + type::{ + name: widget_v2, + fields: closed::{ + name: string, + // widget_v2 has a string for the part_id + part_id: string, + component_ids: { + type: list, + // widget_v2s can still be constructed using v1 components, + // so this can be either a string or an int + element: { one_of: [string, int] } + } + } + } + `, + type: "widget", + value: ` + // Try validating this as widget, widget_latest, widget_v1, and widget_v2 + { + name: "WidgetFoo", + part_id: "177bfe43-e702-44a6-9625-f5eec025ec94", + component_ids: [ + 1843, + 623, + "a890c9ca-1ed4-4f82-b1c7-272a50e256d1" + ], + } + `, + }, + nestedStructs: { + displayName: "Nested structs", + schema: ` + $ion_schema_2_0 + type::{ + name: non_negative_int, + valid_values: range::[0, max], + } + + type::{ + name: package_metadata, + fields: closed::{ + component_namespace: { + occurs: required, + type: string, + }, + component_name: { + occurs: required, + type: string, + }, + version: { + fields: closed::{ + major: non_negative_int, + minor: non_negative_int, + patch: non_negative_int, + } + }, + licenses: { + // Expected to be a list of SPDX license identifiers + occurs: required, + type: list, + container_length: range::[1, max], + element: string, + } + } + }`, + type: "package_metadata", + value: ` + { + component_namespace: "com.amazon.ion", + component_name: "ion-schema-kotlin", + version: { major: 1, minor: 6, patch: 1 }, + licenses: ["Apache-2.0"], + } + ` + }, +} + +initPage(); diff --git a/sandbox.md b/sandbox.md index 72cb74b..c3c3f6b 100644 --- a/sandbox.md +++ b/sandbox.md @@ -12,9 +12,7 @@ title: Give Ion Schema a Try! Predefined examples: