diff --git a/app/web/src/api/sdf/dal/property_editor.ts b/app/web/src/api/sdf/dal/property_editor.ts index 0a47cb89d5..368459ea71 100644 --- a/app/web/src/api/sdf/dal/property_editor.ts +++ b/app/web/src/api/sdf/dal/property_editor.ts @@ -88,6 +88,7 @@ export interface PropertyEditorProp { docLink?: string; isHidden: boolean; isReadonly: boolean; + documentation?: string; } export interface PropertyEditorSchema { diff --git a/app/web/src/components/PropertyEditor/WidgetTextBox.vue b/app/web/src/components/PropertyEditor/WidgetTextBox.vue index af1afa2f01..b2473fe4f6 100644 --- a/app/web/src/components/PropertyEditor/WidgetTextBox.vue +++ b/app/web/src/components/PropertyEditor/WidgetTextBox.vue @@ -51,6 +51,7 @@ const props = defineProps<{ validation?: PropertyEditorValidation; disabled?: boolean; textArea?: boolean; + documentation?: string; }>(); const emit = defineEmits<{ diff --git a/bin/lang-js/src/asset_builder.ts b/bin/lang-js/src/asset_builder.ts index 408696a551..54bf1535b6 100644 --- a/bin/lang-js/src/asset_builder.ts +++ b/bin/lang-js/src/asset_builder.ts @@ -574,6 +574,7 @@ export interface PropDefinition { kind: PropDefinitionKind; docLinkRef?: string; docLink?: string; + documentation?: string; children?: PropDefinition[]; entry?: PropDefinition; widget?: PropWidgetDefinition; @@ -592,6 +593,8 @@ export interface IPropBuilder { setDocLinkRef(ref: string): this; + setDocumentation(ref: string): this; + setDocLink(link: string): this; addChild(child: PropDefinition): this; @@ -621,6 +624,7 @@ export interface IPropBuilder { * const propName = new PropBuilder() * .setName("name") * .setKind("string") + * .setDocumentation("This is the documentation for the prop") * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) * .build(); */ @@ -764,6 +768,21 @@ export class PropBuilder implements IPropBuilder { return this; } + /** + * Sets inline documentation for the prop + * + * @param {string} docs + * + * @returns this + * + * @example + * .setDocumentation("This is documentation for the prop") + */ + setDocumentation(docs: string): this { + this.prop.documentation = docs; + return this; + } + setDocLinkRef(ref: string): this { this.prop.docLinkRef = ref; return this; diff --git a/lib/dal/src/migrations/U2404__add_documentation_to_props.sql b/lib/dal/src/migrations/U2404__add_documentation_to_props.sql new file mode 100644 index 0000000000..1291a0a1e9 --- /dev/null +++ b/lib/dal/src/migrations/U2404__add_documentation_to_props.sql @@ -0,0 +1,64 @@ +-- Add the new columns to the props table. +ALTER TABLE props ADD COLUMN documentation text; + +-- The new prop creation function automatically builds the path using the parent and sets the belongs to relationship. +CREATE OR REPLACE FUNCTION prop_create_v2( + this_tenancy jsonb, + this_visibility jsonb, + this_name text, + this_kind text, + this_widget_kind text, + this_widget_options jsonb, + this_schema_variant_id ident, + this_parent_prop_id ident, + this_documentation text, + OUT object json) AS +$$ +DECLARE + this_tenancy_record tenancy_record_v1; + this_visibility_record visibility_record_v1; + this_new_row props%ROWTYPE; + this_path text; + this_parent_kind text; +BEGIN + this_tenancy_record := tenancy_json_to_columns_v1(this_tenancy); + this_visibility_record := visibility_json_to_columns_v1(this_visibility); + + -- Set the path according to the lineage. If there's no parent, then we know we are the root + -- prop. We also need to ensure that the provided parent is either an object, a map or an + -- array. + IF this_parent_prop_id IS NULL + THEN this_path = this_name; + ELSE + SELECT kind, path || E'\x0B' || this_name INTO STRICT this_parent_kind, this_path + FROM props_v1(this_tenancy, this_visibility) AS props + WHERE props.id = this_parent_prop_id; + + IF this_parent_kind != 'object' AND this_parent_kind != 'array' AND this_parent_kind != 'map' + THEN RAISE EXCEPTION 'prop create: provided parent is not a valid kind: %', this_parent_kind; + END IF; + END IF; + + -- Create and populate the row. + INSERT INTO props (tenancy_workspace_pk, + visibility_change_set_pk, + name, kind, widget_kind, widget_options, schema_variant_id, path, documentation) + VALUES (this_tenancy_record.tenancy_workspace_pk, + this_visibility_record.visibility_change_set_pk, + this_name, this_kind, this_widget_kind, this_widget_options, this_schema_variant_id, this_path, this_documentation) + RETURNING * INTO this_new_row; + + -- Now that we have the row, we can set the parent prop. + IF this_parent_prop_id IS NOT NULL THEN + PERFORM set_belongs_to_v1( + 'prop_belongs_to_prop', + this_tenancy, + this_visibility, + this_new_row.id, + this_parent_prop_id + ); + END IF; + + object := row_to_json(this_new_row); +END; +$$ LANGUAGE PLPGSQL VOLATILE; \ No newline at end of file diff --git a/lib/dal/src/pkg/export.rs b/lib/dal/src/pkg/export.rs index 1e2d70064c..149b8f72d7 100644 --- a/lib/dal/src/pkg/export.rs +++ b/lib/dal/src/pkg/export.rs @@ -743,6 +743,10 @@ impl PkgExporter { builder.try_doc_link(doc_link.as_str())?; } + if let Some(documentation) = tree_node.documentation { + builder.documentation(documentation.as_str()); + } + traversal_stack.push(TraversalStackEntry { builder, prop_id, diff --git a/lib/dal/src/pkg/import.rs b/lib/dal/src/pkg/import.rs index 7f7819f991..e42b3b791a 100644 --- a/lib/dal/src/pkg/import.rs +++ b/lib/dal/src/pkg/import.rs @@ -265,7 +265,7 @@ async fn import_edge( head_component_unique_id, edge_spec.from_socket_name().to_owned(), edge_spec.to_socket_name().to_owned(), - )) + )); } }; @@ -277,7 +277,7 @@ async fn import_edge( tail_component_unique_id, edge_spec.from_socket_name().to_owned(), edge_spec.to_socket_name().to_owned(), - )) + )); } }; @@ -293,7 +293,7 @@ async fn import_edge( None => { return Ok(Some(ImportEdgeSkip::MissingInputSocket( edge_spec.to_socket_name().to_owned(), - ))) + ))); } }; @@ -309,7 +309,7 @@ async fn import_edge( None => { return Ok(Some(ImportEdgeSkip::MissingOutputSocket( edge_spec.from_socket_name().to_owned(), - ))) + ))); } }; @@ -408,7 +408,7 @@ async fn import_component( return Err(PkgError::ComponentMissingSchemaVariant( variant_unique_id.to_owned(), component_spec.name().into(), - )) + )); } } } @@ -2190,6 +2190,7 @@ async fn import_schema_variant( None, *schema_variant.id(), Some(*schema_variant.find_prop(ctx, &["root"]).await?.id()), + None, ) .await? .id(); @@ -3066,6 +3067,7 @@ async fn create_dal_prop( Some(((&data.widget_kind).into(), data.widget_options.to_owned())), schema_variant_id, parent_prop_id, + data.documentation.to_owned(), ) .await .map_err(SiPkgError::visit_prop)?; diff --git a/lib/dal/src/prop.rs b/lib/dal/src/prop.rs index af79872ae8..4a7ac4a10a 100644 --- a/lib/dal/src/prop.rs +++ b/lib/dal/src/prop.rs @@ -259,6 +259,8 @@ pub struct Prop { widget_options: Option, /// A link to external documentation for working with this specific [`Prop`]. doc_link: Option, + /// Embedded documentation for working with this specific [`Prop`]. + documentation: Option, /// A toggle for whether or not the [`Prop`] should be visually hidden. hidden: bool, /// The "path" for a given [`Prop`]. It is a concatenation of [`Prop`] names based on lineage @@ -297,6 +299,7 @@ impl Prop { widget_kind_and_options: Option<(WidgetKind, Option)>, schema_variant_id: SchemaVariantId, parent_prop_id: Option, + documentation: Option, ) -> PropResult { let name = name.as_ref(); let (widget_kind, widget_options) = match widget_kind_and_options { @@ -309,7 +312,7 @@ impl Prop { .await? .pg() .query_one( - "SELECT object FROM prop_create_v2($1, $2, $3, $4, $5, $6, $7, $8)", + "SELECT object FROM prop_create_v2($1, $2, $3, $4, $5, $6, $7, $8, $9)", &[ ctx.tenancy(), ctx.visibility(), @@ -319,6 +322,7 @@ impl Prop { &widget_options.as_ref(), &schema_variant_id, &parent_prop_id, + &documentation, ], ) .await?; @@ -330,6 +334,7 @@ impl Prop { standard_model_accessor!(widget_kind, Enum(WidgetKind), PropResult); standard_model_accessor!(widget_options, Option, PropResult); standard_model_accessor!(doc_link, Option, PropResult); + standard_model_accessor!(documentation, Option, PropResult); standard_model_accessor!(hidden, bool, PropResult); standard_model_accessor!(refers_to_prop_id, Option, PropResult); standard_model_accessor!(diff_func_id, Option, PropResult); diff --git a/lib/dal/src/prop_tree.rs b/lib/dal/src/prop_tree.rs index 47f6b7fffc..657b766412 100644 --- a/lib/dal/src/prop_tree.rs +++ b/lib/dal/src/prop_tree.rs @@ -50,6 +50,7 @@ pub struct PropTreeNode { pub widget_kind: WidgetKind, pub widget_options: Option, pub doc_link: Option, + pub documentation: Option, } impl PropTreeNode { @@ -138,6 +139,9 @@ impl PropTree { for row in rows { let prop_json: serde_json::Value = row.try_get("object")?; let prop: Prop = serde_json::from_value(prop_json)?; + + dbg!(&prop); + if prop.hidden() && !include_hidden { continue; } @@ -170,6 +174,7 @@ impl PropTree { widget_kind: *prop.widget_kind(), widget_options: prop.widget_options().cloned(), doc_link: prop.doc_link().map(|l| l.to_owned()), + documentation: prop.documentation().map(|d| d.to_owned()), visibility_change_set_pk, }; diff --git a/lib/dal/src/property_editor/schema.rs b/lib/dal/src/property_editor/schema.rs index 1f5e547ab4..e9505bac6b 100644 --- a/lib/dal/src/property_editor/schema.rs +++ b/lib/dal/src/property_editor/schema.rs @@ -87,6 +87,7 @@ pub struct PropertyEditorProp { pub kind: PropertyEditorPropKind, pub widget_kind: PropertyEditorPropWidgetKind, pub doc_link: Option, + pub documentation: Option, } impl PropertyEditorProp { @@ -100,6 +101,7 @@ impl PropertyEditorProp { prop.widget_options().map(|v| v.to_owned()), ), doc_link: prop.doc_link().map(Into::into), + documentation: prop.documentation().map(Into::into), } } } diff --git a/lib/dal/src/schema/variant/definition.rs b/lib/dal/src/schema/variant/definition.rs index 002eeb50ad..0f9c1c5587 100644 --- a/lib/dal/src/schema/variant/definition.rs +++ b/lib/dal/src/schema/variant/definition.rs @@ -573,6 +573,9 @@ pub struct PropDefinition { /// An optional documentation link for the [`Prop`](crate::Prop) to be created. #[serde(skip_serializing_if = "Option::is_none")] pub doc_link: Option, + /// An optional set of inline documentation for the [`Prop`](crate::Prop) to be created. + #[serde(skip_serializing_if = "Option::is_none")] + pub documentation: Option, /// If our [`kind`](crate::PropKind) is [`Object`](crate::PropKind::Object), specify the /// child definition(s). #[serde(default)] @@ -612,6 +615,9 @@ impl PropDefinition { if let Some(doc_url) = &self.doc_link { builder.try_doc_link(doc_url.as_str())?; } + if let Some(docs) = &self.documentation { + builder.documentation(docs); + } if let Some(default_value) = &self.default_value { builder.default_value(default_value.to_owned()); } diff --git a/lib/dal/src/schema/variant/root_prop.rs b/lib/dal/src/schema/variant/root_prop.rs index 5bb588271b..ad279b5909 100644 --- a/lib/dal/src/schema/variant/root_prop.rs +++ b/lib/dal/src/schema/variant/root_prop.rs @@ -111,7 +111,7 @@ impl SchemaVariant { ctx: &DalContext, schema_id: SchemaId, ) -> SchemaVariantResult { - let root_prop = Prop::new(ctx, "root", PropKind::Object, None, self.id, None).await?; + let root_prop = Prop::new(ctx, "root", PropKind::Object, None, self.id, None, None).await?; let root_prop_id = *root_prop.id(); self.set_root_prop_id(ctx, Some(root_prop_id)).await?; @@ -127,6 +127,7 @@ impl SchemaVariant { None, self.id, Some(root_prop_id), + None, ) .await?; @@ -137,6 +138,7 @@ impl SchemaVariant { None, self.id, Some(root_prop_id), + None, ) .await? .id(); @@ -179,6 +181,7 @@ impl SchemaVariant { None, schema_variant_id, Some(root_prop_id), + None, ) .await?; leaf_prop.set_hidden(ctx, true).await?; @@ -190,6 +193,7 @@ impl SchemaVariant { None, schema_variant_id, Some(*leaf_prop.id()), + None, ) .await?; leaf_item_prop.set_hidden(ctx, true).await?; @@ -237,6 +241,7 @@ impl SchemaVariant { None, schema_variant_id, Some(root_prop_id), + None, ) .await?; let si_prop_id = *si_prop.id(); @@ -247,6 +252,7 @@ impl SchemaVariant { None, schema_variant_id, Some(si_prop_id), + None, ) .await?; @@ -258,6 +264,7 @@ impl SchemaVariant { None, schema_variant_id, Some(si_prop_id), + None, ) .await?; @@ -287,6 +294,7 @@ impl SchemaVariant { )), schema_variant_id, Some(si_prop_id), + None, ) .await?; @@ -298,6 +306,7 @@ impl SchemaVariant { None, schema_variant_id, Some(si_prop_id), + None, ) .await?; color_prop.set_widget_kind(ctx, WidgetKind::Color).await?; @@ -326,6 +335,7 @@ impl SchemaVariant { None, schema_variant_id, Some(root_prop_id), + None, ) .await?; resource_value_prop.set_hidden(ctx, true).await?; @@ -362,6 +372,7 @@ impl SchemaVariant { None, schema_variant_id, Some(root_prop_id), + None, ) .await?; resource_prop.set_hidden(ctx, true).await?; @@ -374,6 +385,7 @@ impl SchemaVariant { None, schema_variant_id, Some(resource_prop_id), + None, ) .await?; resource_status_prop.set_hidden(ctx, true).await?; @@ -385,6 +397,7 @@ impl SchemaVariant { None, schema_variant_id, Some(resource_prop_id), + None, ) .await?; resource_message_prop.set_hidden(ctx, true).await?; @@ -396,6 +409,7 @@ impl SchemaVariant { None, schema_variant_id, Some(resource_prop_id), + None, ) .await?; resource_logs_prop.set_hidden(ctx, true).await?; @@ -407,6 +421,7 @@ impl SchemaVariant { None, schema_variant_id, Some(*resource_logs_prop.id()), + None, ) .await?; resource_logs_log_prop.set_hidden(ctx, true).await?; @@ -418,6 +433,7 @@ impl SchemaVariant { None, schema_variant_id, Some(resource_prop_id), + None, ) .await?; resource_payload_prop.set_hidden(ctx, true).await?; @@ -429,6 +445,7 @@ impl SchemaVariant { None, schema_variant_id, Some(resource_prop_id), + None, ) .await?; resource_last_synced_prop.set_hidden(ctx, true).await?; @@ -456,6 +473,7 @@ impl SchemaVariant { None, schema_variant_id, Some(code_map_item_prop_id), + None, ) .await?; child_code_prop.set_hidden(ctx, true).await?; @@ -467,6 +485,7 @@ impl SchemaVariant { None, schema_variant_id, Some(code_map_item_prop_id), + None, ) .await?; child_message_prop.set_hidden(ctx, true).await?; @@ -478,6 +497,7 @@ impl SchemaVariant { None, schema_variant_id, Some(code_map_item_prop_id), + None, ) .await?; child_format_prop.set_hidden(ctx, true).await?; @@ -505,6 +525,7 @@ impl SchemaVariant { None, schema_variant_id, Some(qualification_map_item_prop_id), + None, ) .await?; child_qualified_prop.set_hidden(ctx, true).await?; @@ -516,6 +537,7 @@ impl SchemaVariant { None, schema_variant_id, Some(qualification_map_item_prop_id), + None, ) .await?; child_message_prop.set_hidden(ctx, true).await?; @@ -536,6 +558,7 @@ impl SchemaVariant { None, schema_variant_id, Some(root_prop_id), + None, ) .await?; deleted_at.set_hidden(ctx, true).await?; diff --git a/lib/dal/tests/integration_test/internal/attribute/prototype.rs b/lib/dal/tests/integration_test/internal/attribute/prototype.rs index 64644ecb7e..43fd02fa12 100644 --- a/lib/dal/tests/integration_test/internal/attribute/prototype.rs +++ b/lib/dal/tests/integration_test/internal/attribute/prototype.rs @@ -85,6 +85,7 @@ async fn list_for_context_with_a_hash(ctx: &DalContext) { None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -95,6 +96,7 @@ async fn list_for_context_with_a_hash(ctx: &DalContext) { None, schema_variant_id, Some(*albums_prop.id()), + None, ) .await .expect("could not create prop"); @@ -105,6 +107,7 @@ async fn list_for_context_with_a_hash(ctx: &DalContext) { None, schema_variant_id, Some(*album_prop.id()), + None, ) .await .expect("could not create prop"); @@ -349,6 +352,7 @@ async fn remove_least_specific(ctx: &DalContext) { None, schema_variant_id, Some(*domain_prop.id()), + None, ) .await .expect("could not create prop"); @@ -391,6 +395,7 @@ async fn remove_component_specific(ctx: &DalContext) { None, *schema_variant.id(), Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/attribute/prototype_argument.rs b/lib/dal/tests/integration_test/internal/attribute/prototype_argument.rs index 229c5b5f78..32be684345 100644 --- a/lib/dal/tests/integration_test/internal/attribute/prototype_argument.rs +++ b/lib/dal/tests/integration_test/internal/attribute/prototype_argument.rs @@ -33,6 +33,7 @@ async fn create_and_list_for_attribute_prototype(ctx: &DalContext) { None, *schema_variant.id(), Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -43,6 +44,7 @@ async fn create_and_list_for_attribute_prototype(ctx: &DalContext) { None, *schema_variant.id(), Some(*object_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/attribute/value.rs b/lib/dal/tests/integration_test/internal/attribute/value.rs index 68d629f164..5d63421b3d 100644 --- a/lib/dal/tests/integration_test/internal/attribute/value.rs +++ b/lib/dal/tests/integration_test/internal/attribute/value.rs @@ -27,6 +27,7 @@ async fn update_for_context_simple(ctx: &DalContext) { None, *schema_variant.id(), Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -183,6 +184,7 @@ async fn insert_for_context_simple(ctx: &DalContext) { None, *schema_variant.id(), Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -193,6 +195,7 @@ async fn insert_for_context_simple(ctx: &DalContext) { None, *schema_variant.id(), Some(*array_prop.id()), + None, ) .await .expect("could not create prop"); @@ -293,6 +296,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -303,6 +307,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*address_prop.id()), + None, ) .await .expect("could not create prop"); @@ -313,6 +318,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*streets_prop.id()), + None, ) .await .expect("could not create prop"); @@ -323,6 +329,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*address_prop.id()), + None, ) .await .expect("could not create prop"); @@ -333,6 +340,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*address_prop.id()), + None, ) .await .expect("could not create prop"); @@ -343,6 +351,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*address_prop.id()), + None, ) .await .expect("could not create prop"); @@ -353,6 +362,7 @@ async fn update_for_context_object(ctx: &DalContext) { None, schema_variant_id, Some(*tags_prop.id()), + None, ) .await .expect("could not create prop"); @@ -557,6 +567,7 @@ async fn insert_for_context_creates_array_in_final_context(ctx: &DalContext) { None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -567,6 +578,7 @@ async fn insert_for_context_creates_array_in_final_context(ctx: &DalContext) { None, schema_variant_id, Some(*array_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/attribute/view.rs b/lib/dal/tests/integration_test/internal/attribute/view.rs index ea32b5657b..418cc4071f 100644 --- a/lib/dal/tests/integration_test/internal/attribute/view.rs +++ b/lib/dal/tests/integration_test/internal/attribute/view.rs @@ -25,6 +25,7 @@ async fn schema_variant_specific(ctx: &DalContext) { None, *schema_variant.id(), Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component.rs b/lib/dal/tests/integration_test/internal/component.rs index 68cf6935f4..1d8f9cafc3 100644 --- a/lib/dal/tests/integration_test/internal/component.rs +++ b/lib/dal/tests/integration_test/internal/component.rs @@ -246,6 +246,7 @@ async fn dependent_values_resource_intelligence(mut octx: DalContext) { None, *noctua_schema_variant.id(), Some(noctua_root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/code.rs b/lib/dal/tests/integration_test/internal/component/code.rs index 1644eed02f..b987f03320 100644 --- a/lib/dal/tests/integration_test/internal/component/code.rs +++ b/lib/dal/tests/integration_test/internal/component/code.rs @@ -33,6 +33,7 @@ async fn add_code_generation_and_list_code_views(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -178,6 +179,7 @@ async fn all_code_generation_attribute_values(ctx: &DalContext) { None, *navi_schema_variant.id(), Some(navi_root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -198,6 +200,7 @@ async fn all_code_generation_attribute_values(ctx: &DalContext) { None, *kru_schema_variant.id(), Some(kru_root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/qualification.rs b/lib/dal/tests/integration_test/internal/component/qualification.rs index eef3689d2c..d37b3fe91d 100644 --- a/lib/dal/tests/integration_test/internal/component/qualification.rs +++ b/lib/dal/tests/integration_test/internal/component/qualification.rs @@ -29,6 +29,7 @@ async fn add_and_list_qualifications(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/validation.rs b/lib/dal/tests/integration_test/internal/component/validation.rs index 00ae74fa31..9f1fb596a3 100644 --- a/lib/dal/tests/integration_test/internal/component/validation.rs +++ b/lib/dal/tests/integration_test/internal/component/validation.rs @@ -34,6 +34,7 @@ async fn check_validations_for_component(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -44,6 +45,7 @@ async fn check_validations_for_component(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -339,6 +341,7 @@ async fn check_js_validation_for_component(ctx: &DalContext) { None, *schema_variant.id(), Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/view.rs b/lib/dal/tests/integration_test/internal/component/view.rs index 71e30026b8..d5a7907478 100644 --- a/lib/dal/tests/integration_test/internal/component/view.rs +++ b/lib/dal/tests/integration_test/internal/component/view.rs @@ -36,6 +36,7 @@ pub async fn create_schema_with_object_and_string_prop( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -46,6 +47,7 @@ pub async fn create_schema_with_object_and_string_prop( None, schema_variant_id, Some(*queen_prop.id()), + None, ) .await .expect("could not create prop"); @@ -56,6 +58,7 @@ pub async fn create_schema_with_object_and_string_prop( None, schema_variant_id, Some(*queen_prop.id()), + None, ) .await .expect("could not create prop"); @@ -111,6 +114,7 @@ pub async fn create_schema_with_nested_objects_and_string_prop( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -121,6 +125,7 @@ pub async fn create_schema_with_nested_objects_and_string_prop( None, schema_variant_id, Some(*queen_prop.id()), + None, ) .await .expect("could not create prop"); @@ -131,6 +136,7 @@ pub async fn create_schema_with_nested_objects_and_string_prop( None, schema_variant_id, Some(*queen_prop.id()), + None, ) .await .expect("could not create prop"); @@ -141,6 +147,7 @@ pub async fn create_schema_with_nested_objects_and_string_prop( None, schema_variant_id, Some(*queen_prop.id()), + None, ) .await .expect("could not create prop"); @@ -151,6 +158,7 @@ pub async fn create_schema_with_nested_objects_and_string_prop( None, schema_variant_id, Some(*pressure_prop.id()), + None, ) .await .expect("could not create prop"); @@ -199,6 +207,7 @@ pub async fn create_schema_with_string_props( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -209,6 +218,7 @@ pub async fn create_schema_with_string_props( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -249,6 +259,7 @@ pub async fn create_schema_with_array_of_string_props( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -259,6 +270,7 @@ pub async fn create_schema_with_array_of_string_props( None, schema_variant_id, Some(*sammy_prop.id()), + None, ) .await .expect("could not create prop"); @@ -312,6 +324,7 @@ pub async fn create_schema_with_nested_array_objects( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -322,6 +335,7 @@ pub async fn create_schema_with_nested_array_objects( None, schema_variant_id, Some(*sammy_prop.id()), + None, ) .await .expect("could not create prop"); @@ -332,6 +346,7 @@ pub async fn create_schema_with_nested_array_objects( None, schema_variant_id, Some(*album_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -342,6 +357,7 @@ pub async fn create_schema_with_nested_array_objects( None, schema_variant_id, Some(*album_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -352,6 +368,7 @@ pub async fn create_schema_with_nested_array_objects( None, schema_variant_id, Some(*songs_array_prop.id()), + None, ) .await .expect("could not create prop"); @@ -401,6 +418,7 @@ pub async fn create_simple_map(ctx: &DalContext) -> (Schema, SchemaVariant, Prop None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -411,6 +429,7 @@ pub async fn create_simple_map(ctx: &DalContext) -> (Schema, SchemaVariant, Prop None, schema_variant_id, Some(*album_prop.id()), + None, ) .await .expect("could not create prop"); @@ -464,6 +483,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -474,6 +494,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(*sammy_prop.id()), + None, ) .await .expect("could not create prop"); @@ -484,6 +505,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(*album_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -494,6 +516,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(*album_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -504,6 +527,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(*songs_array_prop.id()), + None, ) .await .expect("could not create prop"); @@ -514,6 +538,7 @@ pub async fn create_schema_with_nested_array_objects_and_a_map( None, schema_variant_id, Some(*song_map_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/view/complex_func.rs b/lib/dal/tests/integration_test/internal/component/view/complex_func.rs index a9761dc45e..2ef140f20c 100644 --- a/lib/dal/tests/integration_test/internal/component/view/complex_func.rs +++ b/lib/dal/tests/integration_test/internal/component/view/complex_func.rs @@ -30,6 +30,7 @@ async fn nested_object_prop_with_complex_func(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -40,6 +41,7 @@ async fn nested_object_prop_with_complex_func(ctx: &DalContext) { None, schema_variant_id, Some(*ragnarok_prop.id()), + None, ) .await .expect("could not create prop"); @@ -50,6 +52,7 @@ async fn nested_object_prop_with_complex_func(ctx: &DalContext) { None, schema_variant_id, Some(*ragnarok_prop.id()), + None, ) .await .expect("could not create prop"); @@ -338,6 +341,7 @@ async fn map_with_object_entries_and_complex_funcs(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -348,6 +352,7 @@ async fn map_with_object_entries_and_complex_funcs(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -361,6 +366,7 @@ async fn map_with_object_entries_and_complex_funcs(ctx: &DalContext) { None, schema_variant_id, Some(map_prop_id), + None, ) .await .expect("could not create prop"); @@ -371,6 +377,7 @@ async fn map_with_object_entries_and_complex_funcs(ctx: &DalContext) { None, schema_variant_id, Some(*map_item_prop.id()), + None, ) .await .expect("could not create prop"); @@ -381,6 +388,7 @@ async fn map_with_object_entries_and_complex_funcs(ctx: &DalContext) { None, schema_variant_id, Some(*map_item_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/component/view/properties.rs b/lib/dal/tests/integration_test/internal/component/view/properties.rs index 43848447ad..8daf4f9d9f 100644 --- a/lib/dal/tests/integration_test/internal/component/view/properties.rs +++ b/lib/dal/tests/integration_test/internal/component/view/properties.rs @@ -32,6 +32,7 @@ async fn drop_subtree_using_component_view_properties(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/prop.rs b/lib/dal/tests/integration_test/internal/prop.rs index 2c395b2827..632cbaac2e 100644 --- a/lib/dal/tests/integration_test/internal/prop.rs +++ b/lib/dal/tests/integration_test/internal/prop.rs @@ -21,6 +21,7 @@ async fn new(ctx: &DalContext) { None, schema_variant_id, Some(*domain_prop.id()), + None, ) .await .expect("cannot create prop"); @@ -47,6 +48,7 @@ async fn parent_props(ctx: &DalContext) { None, schema_variant_id, Some(*domain_prop.id()), + None, ) .await .expect("cannot create prop"); @@ -57,6 +59,7 @@ async fn parent_props(ctx: &DalContext) { None, schema_variant_id, Some(*parent_prop.id()), + None, ) .await .expect("cannot create prop"); @@ -94,6 +97,7 @@ async fn parent_props_wrong_prop_kinds(ctx: &DalContext) { None, *schema_variant.id(), Some(*root_prop_id), + None, ) .await .expect("cannot create prop"); @@ -104,6 +108,7 @@ async fn parent_props_wrong_prop_kinds(ctx: &DalContext) { None, *schema_variant.id(), Some(*parent_prop.id()), + None, ) .await; result.expect_err("should have errored, and it did not"); diff --git a/lib/dal/tests/integration_test/internal/property_editor.rs b/lib/dal/tests/integration_test/internal/property_editor.rs index 1bed2f1c0b..041da3efc1 100644 --- a/lib/dal/tests/integration_test/internal/property_editor.rs +++ b/lib/dal/tests/integration_test/internal/property_editor.rs @@ -25,6 +25,7 @@ async fn property_editor_schema(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -35,6 +36,7 @@ async fn property_editor_schema(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -45,6 +47,7 @@ async fn property_editor_schema(ctx: &DalContext) { None, schema_variant_id, Some(*exposed_ports_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/provider/inter_component.rs b/lib/dal/tests/integration_test/internal/provider/inter_component.rs index 1ca61141d1..bb04933071 100644 --- a/lib/dal/tests/integration_test/internal/provider/inter_component.rs +++ b/lib/dal/tests/integration_test/internal/provider/inter_component.rs @@ -332,6 +332,7 @@ async fn setup_esp(ctx: &DalContext) -> (ComponentBag, PropId, PropId) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -342,6 +343,7 @@ async fn setup_esp(ctx: &DalContext) -> (ComponentBag, PropId, PropId) { None, schema_variant_id, Some(*object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -352,6 +354,7 @@ async fn setup_esp(ctx: &DalContext) -> (ComponentBag, PropId, PropId) { None, schema_variant_id, Some(*object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -433,6 +436,7 @@ async fn setup_swings(ctx: &DalContext) -> (ComponentBag, PropId) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -499,6 +503,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *source_schema_variant.id(), Some(source_root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -509,6 +514,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *source_schema_variant.id(), Some(*source_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -519,6 +525,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *source_schema_variant.id(), Some(*source_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -575,6 +582,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *destination_schema_variant.id(), Some(destination_root.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -585,6 +593,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *destination_schema_variant.id(), Some(*destination_parent_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -595,6 +604,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *destination_schema_variant.id(), Some(*destination_object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -605,6 +615,7 @@ async fn with_deep_data_structure(ctx: &DalContext) { None, *destination_schema_variant.id(), Some(*destination_object_prop.id()), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/provider/intra_component.rs b/lib/dal/tests/integration_test/internal/provider/intra_component.rs index 779a312a93..9945afcbaf 100644 --- a/lib/dal/tests/integration_test/internal/provider/intra_component.rs +++ b/lib/dal/tests/integration_test/internal/provider/intra_component.rs @@ -35,6 +35,7 @@ async fn intra_component_identity_update(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); @@ -45,6 +46,7 @@ async fn intra_component_identity_update(ctx: &DalContext) { None, schema_variant_id, Some(*object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -55,6 +57,7 @@ async fn intra_component_identity_update(ctx: &DalContext) { None, schema_variant_id, Some(*object_prop.id()), + None, ) .await .expect("could not create prop"); @@ -338,6 +341,7 @@ async fn intra_component_custom_func_update_to_external_provider(ctx: &DalContex None, *schema_variant.id(), Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/dal/tests/integration_test/internal/validation_resolver.rs b/lib/dal/tests/integration_test/internal/validation_resolver.rs index 47373fec30..ce5fefb279 100644 --- a/lib/dal/tests/integration_test/internal/validation_resolver.rs +++ b/lib/dal/tests/integration_test/internal/validation_resolver.rs @@ -27,6 +27,7 @@ async fn new(ctx: &DalContext) { None, schema_variant_id, Some(root_prop.domain_prop_id), + None, ) .await .expect("could not create prop"); diff --git a/lib/sdf-server/src/server/service/ts_types/asset_builder.d.ts b/lib/sdf-server/src/server/service/ts_types/asset_builder.d.ts index 2437289c39..9e00f7592c 100644 --- a/lib/sdf-server/src/server/service/ts_types/asset_builder.d.ts +++ b/lib/sdf-server/src/server/service/ts_types/asset_builder.d.ts @@ -198,7 +198,7 @@ declare class ValidationBuilder implements IValidationBuilder { setKind(kind: ValidationKind): this; setUpperBound(value: number): this; } -type PropWidgetDefinitionKind = "array" | "checkbox" | "color" | "comboBox" | "header" | "map" | "secret" | "select" | "text" | "textArea" | "codeEditor"; +type PropWidgetDefinitionKind = "array" | "checkbox" | "codeEditor" | "color" | "comboBox" | "header" | "map" | "secret" | "select" | "text" | "textArea"; interface Option { label: string; value: string; @@ -226,7 +226,7 @@ declare class PropWidgetDefinitionBuilder implements IPropWidgetDefinitionBuilde /** * The type of widget * - * @param {string} kind [array | checkbox | color | comboBox | header | map | secret | select | text | textArea] + * @param {string} kind [array | checkbox | color | comboBox | header | map | secret | select | text | textArea | codeEditor] * * @returns this * @@ -237,7 +237,7 @@ declare class PropWidgetDefinitionBuilder implements IPropWidgetDefinitionBuilde /** * Add an option when using a comboBox * - * @param {string} key - the value displayed in the comboBoxx + * @param {string} key - the value displayed in the comboBox * @param {string} value - the value the prop is set to * * @returns this @@ -336,6 +336,7 @@ interface PropDefinition { kind: PropDefinitionKind; docLinkRef?: string; docLink?: string; + documentation?: string; children?: PropDefinition[]; entry?: PropDefinition; widget?: PropWidgetDefinition; @@ -349,6 +350,7 @@ interface IPropBuilder { setName(name: string): this; setKind(kind: PropDefinitionKind): this; setDocLinkRef(ref: string): this; + setDocumentation(ref: string): this; setDocLink(link: string): this; addChild(child: PropDefinition): this; setEntry(entry: PropDefinition): this; @@ -367,6 +369,7 @@ interface IPropBuilder { * const propName = new PropBuilder() * .setName("name") * .setKind("string") + * .setDocumentation("This is the documentation for the prop") * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) * .build(); */ @@ -458,6 +461,17 @@ declare class PropBuilder implements IPropBuilder { * .setDocLink("https://www.systeminit.com/") */ setDocLink(link: string): this; + /** + * Sets inline documentation for the prop + * + * @param {string} docs + * + * @returns this + * + * @example + * .setDocumentation("This is documentation for the prop") + */ + setDocumentation(docs: string): this; setDocLinkRef(ref: string): this; /** * Whether the prop should be displayed in th UI or not diff --git a/lib/sdf-server/src/server/service/ts_types/asset_types.d.ts b/lib/sdf-server/src/server/service/ts_types/asset_types.d.ts new file mode 100644 index 0000000000..ba3c012f75 --- /dev/null +++ b/lib/sdf-server/src/server/service/ts_types/asset_types.d.ts @@ -0,0 +1,610 @@ +declare module "asset_builder" { + export type ValueFromKind = "inputSocket" | "outputSocket" | "prop"; + export interface ValueFrom { + kind: ValueFromKind; + socket_name?: string; + prop_path?: string[]; + } + export interface IValueFromBuilder { + setKind(kind: ValueFromKind): this; + setSocketName(name: string): this; + setPropPath(path: string[]): this; + build(): ValueFrom; + } + /** + * Gets a value from a socket or prop + * + * @example + * const value = new ValueFromBuilder() + * .setKind("prop") + * .setPropPath(["root", "si", "name"]) + * .build() + */ + export class ValueFromBuilder implements IValueFromBuilder { + valueFrom: ValueFrom; + constructor(); + /** + * The type of the builder + * + * @param {string} kind [inputSocket | outputSocket | prop] + * + * @returns this + * + * @example + * .setKind("prop") + */ + setKind(kind: ValueFromKind): this; + /** + * Specify the socket name if using an inputSocket or outputSocket + * + * @param {string} name + * + * @returns this + * + * @example + * .setSocketName("Region") + */ + setSocketName(name: string): this; + /** + * Specify the prop path if using a prop + * + * @param {string[]} path - a list of strings that represent the path to the prop + * + * @returns this + * + * @example + * .setPropPath(["root", "si", "name"]) + */ + setPropPath(path: string[]): this; + /** + * Build the object + * + * @example + * .build() + */ + build(): ValueFrom; + } + export type SocketDefinitionArityType = "many" | "one"; + export interface SocketDefinition { + name: string; + arity: SocketDefinitionArityType; + uiHidden?: boolean; + valueFrom?: ValueFrom; + } + export interface ISocketDefinitionBuilder { + setName(name: string): this; + setArity(arity: SocketDefinitionArityType): this; + setUiHidden(hidden: boolean): this; + setValueFrom(valueFrom: ValueFrom): this; + build(): SocketDefinition; + } + /** + * Defines an input or output socket for passing values between components + * + * @example + * const regionSocket = new SocketDefinitionBuilder() + * .setName("Region") + * .setArity("one") + * .build(); + */ + export class SocketDefinitionBuilder implements ISocketDefinitionBuilder { + socket: SocketDefinition; + constructor(); + /** + * Build the object + * + * @example + * .build() + */ + build(): SocketDefinition; + /** + * Specify the prop path if using a prop + * + * @param {string} arity - [one | many] + * + * @returns this + * + * @example + * .setArity("one") + */ + setArity(arity: SocketDefinitionArityType): this; + /** + * The name of the socket. Note that this will be used to connect sockets + * and to reference the socket within the asset. + * + * @param {string} name + * + * @returns this + * + * @example + * .setName("Subnet ID") + */ + setName(name: string): this; + /** + * Should this socket show in the UI. Note that the socket can still be connected when the component is placed in a frame. + * + * @param {boolean} hidden + * + * @returns this + * + * @example + * .setName("Subnet ID") + */ + setUiHidden(hidden: boolean): this; + /** + * Set the value of this socket using a ValueFromBuilder + * + * @param {ValueFrom} valueFrom + * + * @returns this + * + * @example + * .setValueFrom(new ValueFromBuilder() + * .setKind("inputSocket") + * .setSocketName("Region") + * .build()) + */ + setValueFrom(valueFrom: ValueFrom): this; + } + export type ValidationKind = "customValidation" | "integerIsBetweenTwoIntegers" | "integerIsNotEmpty" | "stringEquals" | "stringHasPrefix" | "stringInStringArray" | "stringIsHexColor" | "stringIsNotEmpty" | "stringIsValidIpAddr"; + export interface Validation { + kind: ValidationKind; + funcUniqueId?: Record; + lowerBound?: number; + upperBound?: number; + expected?: string[]; + displayExpected?: boolean; + } + export interface IValidationBuilder { + setKind(kind: ValidationKind): this; + addFuncUniqueId(key: string, value: unknown): this; + setLowerBound(value: number): this; + setUpperBound(value: number): this; + addExpected(expected: string): this; + setDisplayExpected(display: boolean): this; + build(): Validation; + } + /** + * Validates a prop using a function or from a list of common validations + * + * @example + * const validation = new ValidationBuilder() + * .setKind("stringIsNotEmpty") + * .build() + */ + export class ValidationBuilder implements IValidationBuilder { + validation: Validation; + constructor(); + addFuncUniqueId(key: string, value: unknown): this; + /** + * Build the object + * + * @example + * .build() + */ + build(): Validation; + setDisplayExpected(display: boolean): this; + addExpected(expected: string): this; + setLowerBound(value: number): this; + /** + * The type of validation + * + * @param {string} kind [customValidation | integerIsBetweenTwoIntegers | integerIsNotEmpty | stringEquals | stringHasPrefix | stringInStringArray | stringIsHexColor | stringIsNotEmpty | stringIsValidIpAddr] + * + * @returns this + * + * @example + * .setKind("integerIsNotEmpty") + */ + setKind(kind: ValidationKind): this; + setUpperBound(value: number): this; + } + export type PropWidgetDefinitionKind = "array" | "checkbox" | "codeEditor" | "color" | "comboBox" | "header" | "map" | "secret" | "select" | "text" | "textArea"; + export interface Option { + label: string; + value: string; + } + export interface PropWidgetDefinition { + kind: PropWidgetDefinitionKind; + options: Option[]; + } + export interface IPropWidgetDefinitionBuilder { + setKind(kind: string): this; + addOption(key: string, value: string): this; + build(): PropWidgetDefinition; + } + /** + * Create a widget for interacting with a prop that is displayed in the modelling view. + * + * @example + * const validation = new PropWidgetDefinitionBuilder() + * .setKind("text") + * .build() + */ + export class PropWidgetDefinitionBuilder implements IPropWidgetDefinitionBuilder { + propWidget: PropWidgetDefinition; + constructor(); + /** + * The type of widget + * + * @param {string} kind [array | checkbox | color | comboBox | header | map | secret | select | text | textArea | codeEditor] + * + * @returns this + * + * @example + * .setKind("color") + */ + setKind(kind: PropWidgetDefinitionKind): this; + /** + * Add an option when using a comboBox + * + * @param {string} key - the value displayed in the comboBox + * @param {string} value - the value the prop is set to + * + * @returns this + * + * @example + * .setOption("us-east-2 - US East (Ohio)", "us-east-2") + */ + addOption(key: string, value: string): this; + /** + * Build the object + * + * @example + * .build() + */ + build(): PropWidgetDefinition; + } + export interface MapKeyFunc { + key: string; + valueFrom?: ValueFrom; + } + export interface IMapKeyFuncBuilder { + setKey(key: string): this; + setValueFrom(valueFrom: ValueFrom): this; + build(): MapKeyFunc; + } + /** + * Used to add a value to a map + * + * @example + * const mapButton = new MapKeyFuncBuilder() + * .setKey("Name") + * .build() + */ + export class MapKeyFuncBuilder implements IMapKeyFuncBuilder { + mapKeyFunc: MapKeyFunc; + constructor(); + /** + * Build the object + * + * @example + * .build() + */ + build(): MapKeyFunc; + /** + * Set the value of the key for the map entry + * + * @param {string} key - the name of the key + * + * @returns this + * + * @example + * .setKey("Name") + */ + setKey(key: string): this; + /** + * Set the value of this key from a ValueFromBuilder + * + * @param {ValueFrom} valueFrom + * + * @returns this + * + * @example + * .setValueFrom(new ValueFromBuilder() + * .setKind("prop") + * .setPropPath(["root", "si", "name"]) + * .build()) + */ + setValueFrom(valueFrom: ValueFrom): this; + } + export type SiPropValueFromDefinitionKind = "color" | "name" | "resourcePayload"; + export interface SiPropValueFromDefinition { + kind: SiPropValueFromDefinitionKind; + valueFrom: ValueFrom; + } + export interface ISiPropValueFromDefinitionBuilder { + setKind(kind: SiPropValueFromDefinitionKind): this; + setValueFrom(valueFrom: ValueFrom): this; + build(): SiPropValueFromDefinition; + } + export class SiPropValueFromDefinitionBuilder implements ISiPropValueFromDefinitionBuilder { + definition: SiPropValueFromDefinition; + constructor(); + /** + * Build the object + * + * @example + * .build() + */ + build(): SiPropValueFromDefinition; + setKind(kind: SiPropValueFromDefinitionKind): this; + setValueFrom(valueFrom: ValueFrom): this; + } + export type PropDefinitionKind = "array" | "boolean" | "integer" | "map" | "object" | "string"; + export interface PropDefinition { + name: string; + kind: PropDefinitionKind; + docLinkRef?: string; + docLink?: string; + documentation?: string; + children?: PropDefinition[]; + entry?: PropDefinition; + widget?: PropWidgetDefinition; + valueFrom?: ValueFrom; + hidden?: boolean; + defaultValue?: any; + validations?: Validation[]; + mapKeyFuncs?: MapKeyFunc[]; + } + export interface IPropBuilder { + setName(name: string): this; + setKind(kind: PropDefinitionKind): this; + setDocLinkRef(ref: string): this; + setDocumentation(ref: string): this; + setDocLink(link: string): this; + addChild(child: PropDefinition): this; + setEntry(entry: PropDefinition): this; + setWidget(widget: PropWidgetDefinition): this; + setValueFrom(valueFrom: ValueFrom): this; + setHidden(hidden: boolean): this; + setDefaultValue(value: any): this; + addValidation(validation: Validation): this; + addMapKeyFunc(func: MapKeyFunc): this; + build(): PropDefinition; + } + /** + * Creates a prop to attach values to an asset + * + * @example + * const propName = new PropBuilder() + * .setName("name") + * .setKind("string") + * .setDocumentation("This is the documentation for the prop") + * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) + * .build(); + */ + export class PropBuilder implements IPropBuilder { + prop: PropDefinition; + constructor(); + /** + * Adds a child to an object type prop + * + * @param {PropDefinition} child + * + * @returns this + * + * @example + * .addChild(new PropBuilder() + * .setKind("string") + * .setName("sweetChildProp") + * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) + * .build()) + */ + addChild(child: PropDefinition): this; + /** + * Adds an entry to array or map type props + * + * @param {PropDefinition} entry + * + * @returns this + * + * @example + * .setEntry(new PropBuilder() + * .setKind("string") + * .setName("iamanentryprop") + * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) + * .build()) + */ + setEntry(entry: PropDefinition): this; + /** + * Add a button for putting entries into maps + * + * @param {MapKeyFunc} func + * + * @returns this + * + * @example + * .addMapKeyFunc(new MapKeyFuncBuilder() + * .setKey("Name") + * .build() + */ + addMapKeyFunc(func: MapKeyFunc): this; + /** + * Add functions to validate the value of the prop + * + * @param {Validation} validation + * + * @returns this + * + * @example + * .addValidation(new ValidationBuilder() + * .setKind("stringIsNotEmpty") + * .build()) + */ + addValidation(validation: Validation): this; + /** + * Build the object + * + * @example + * .build() + */ + build(): PropDefinition; + /** + * Set a value to be automatically populated in the prop + * + * @param {any} value + * + * @returns this + * + * @example + * .setDefaultValue("cats") + */ + setDefaultValue(value: any): this; + /** + * Set a link to external documentation that will appear beneath the prop + * + * @param {string} link + * + * @returns this + * + * @example + * .setDocLink("https://www.systeminit.com/") + */ + setDocLink(link: string): this; + /** + * Sets inline documentation for the prop + * + * @param {string} docs + * + * @returns this + * + * @example + * .setDocumentation("This is documentation for the prop") + */ + setDocumentation(docs: string): this; + setDocLinkRef(ref: string): this; + /** + * Whether the prop should be displayed in th UI or not + * + * @param {boolean} hidden + * + * @returns this + * + * @example + * .setHidden(true) + */ + setHidden(hidden: boolean): this; + /** + * The type of the prop + * + * @param {string} kind [array | boolean | integer | map | object | string] + * + * @returns this + * + * @example + * .setKind("text") + */ + setKind(kind: PropDefinitionKind): this; + /** + * The prop name. This will appear in the model UI + * + * @param {string} name - the name of the prop + * + * @returns this + * + * @example + * .setName("Region") + */ + setName(name: string): this; + /** + * Set the value of this prop using a ValueFromBuilder + * + * @param {ValueFrom} valueFrom + * + * @returns this + * + * @example + * .setValueFrom(new ValueFromBuilder() + * .setKind("inputSocket") + * .setSocketName("Region") + * .build()) + */ + setValueFrom(valueFrom: ValueFrom): this; + /** + * The type of widget for the prop, determing how it is displayed in the UI + * + * @param {PropWidgetDefinition} widget + * + * @returns this + * + * @example + * setWidget(new PropWidgetDefinitionBuilder() + * .setKind("text") + * .build()) + */ + setWidget(widget: PropWidgetDefinition): this; + } + export interface SecretPropDefinition extends PropDefinition { + hasInputSocket: boolean; + } + export interface ISecretPropBuilder { + setName(name: string): this; + setSecretKind(kind: string): this; + setDocLinkRef(ref: string): this; + setDocLink(link: string): this; + addValidation(validation: Validation): this; + skipInputSocket(): this; + build(): SecretPropDefinition; + } + export class SecretPropBuilder implements ISecretPropBuilder { + prop: SecretPropDefinition; + constructor(); + setName(name: string): this; + setSecretKind(kind: string): this; + setDocLinkRef(ref: string): this; + setDocLink(link: string): this; + addValidation(validation: Validation): this; + skipInputSocket(): this; + build(): SecretPropDefinition; + } + export interface SecretDefinition { + name: string; + props: PropDefinition[]; + } + export interface ISecretDefinitionBuilder { + addProp(prop: PropDefinition): this; + build(): SecretDefinition; + } + export class SecretDefinitionBuilder implements ISecretDefinitionBuilder { + definition: SecretDefinition; + constructor(); + setName(name: string): this; + addProp(prop: PropDefinition): this; + build(): SecretDefinition; + } + export interface Asset { + props: PropDefinition[]; + secretProps: SecretPropDefinition[]; + secretDefinition?: PropDefinition[]; + resourceProps: PropDefinition[]; + siPropValueFroms: SiPropValueFromDefinition[]; + inputSockets: SocketDefinition[]; + outputSockets: SocketDefinition[]; + docLinks: Record; + } + export interface IAssetBuilder { + addProp(prop: PropDefinition): this; + addSecretProp(prop: SecretPropDefinition): this; + defineSecret(definition: SecretDefinition): this; + addResourceProp(prop: PropDefinition): this; + addInputSocket(socket: SocketDefinition): this; + addOutputSocket(socket: SocketDefinition): this; + addSiPropValueFrom(siPropValueFrom: SiPropValueFromDefinition): this; + addDocLink(key: string, value: string): this; + build(): Asset; + } + export class AssetBuilder implements IAssetBuilder { + asset: Asset; + constructor(); + addProp(prop: PropDefinition): this; + addSecretProp(prop: SecretPropDefinition): this; + defineSecret(definition: SecretDefinition): this; + addResourceProp(prop: PropDefinition): this; + addInputSocket(socket: SocketDefinition): this; + addOutputSocket(socket: SocketDefinition): this; + addSiPropValueFrom(siPropValueFrom: SiPropValueFromDefinition): this; + addDocLink(key: string, value: string): this; + build(): Asset; + } +} diff --git a/lib/sdf-server/src/server/service/ts_types/asset_types_with_secrets.d.ts b/lib/sdf-server/src/server/service/ts_types/asset_types_with_secrets.d.ts index 5f94990166..4825014bd7 100644 --- a/lib/sdf-server/src/server/service/ts_types/asset_types_with_secrets.d.ts +++ b/lib/sdf-server/src/server/service/ts_types/asset_types_with_secrets.d.ts @@ -336,6 +336,7 @@ interface PropDefinition { kind: PropDefinitionKind; docLinkRef?: string; docLink?: string; + documentation? string; children?: PropDefinition[]; entry?: PropDefinition; widget?: PropWidgetDefinition; @@ -350,6 +351,7 @@ interface IPropBuilder { setKind(kind: PropDefinitionKind): this; setDocLinkRef(ref: string): this; setDocLink(link: string): this; + setDocumentation(ref: string): this; addChild(child: PropDefinition): this; setEntry(entry: PropDefinition): this; setWidget(widget: PropWidgetDefinition): this; @@ -384,6 +386,7 @@ declare class PropBuilder implements IPropBuilder { * .addChild(new PropBuilder() * .setKind("string") * .setName("sweetChildProp") + * .setDocumentation("This is the documentation for the prop") * .setWidget(new PropWidgetDefinitionBuilder().setKind("text").build()) * .build()) */ @@ -458,6 +461,17 @@ declare class PropBuilder implements IPropBuilder { * .setDocLink("https://www.systeminit.com/") */ setDocLink(link: string): this; + /** + * Sets inline documentation for the prop + * + * @param {string} docs + * + * @returns this + * + * @example + * .setDocumentation("This is documentation for the prop") + */ + setDocumentation(docs: string): this; setDocLinkRef(ref: string): this; /** * Whether the prop should be displayed in th UI or not diff --git a/lib/si-pkg/src/node/prop.rs b/lib/si-pkg/src/node/prop.rs index d1e3638bbe..4313d3c83f 100644 --- a/lib/si-pkg/src/node/prop.rs +++ b/lib/si-pkg/src/node/prop.rs @@ -6,8 +6,8 @@ use std::{ use url::Url; use object_tree::{ - read_key_value_line, read_key_value_line_opt, write_key_value_line, GraphError, NameStr, - NodeChild, NodeKind, NodeWithChildren, ReadBytes, WriteBytes, + read_key_value_line, read_key_value_line_opt, write_key_value_line, write_key_value_line_opt, + GraphError, NameStr, NodeChild, NodeKind, NodeWithChildren, ReadBytes, WriteBytes, }; use crate::{spec::PropSpecData, PropSpec, PropSpecWidgetKind}; @@ -22,6 +22,7 @@ const KEY_WIDGET_KIND_STR: &str = "widget_kind"; const KEY_WIDGET_OPTIONS_STR: &str = "widget_options"; const KEY_HIDDEN_STR: &str = "hidden"; const KEY_DOC_LINK_STR: &str = "doc_link"; +const KEY_DOCUMENTATION_STR: &str = "documentation"; const KEY_UNIQUE_ID_STR: &str = "unique_id"; const PROP_TY_STRING: &str = "string"; @@ -40,6 +41,7 @@ pub struct PropNodeData { pub widget_options: Option, pub doc_link: Option, pub hidden: bool, + pub documentation: Option, } #[remain::sorted] @@ -152,6 +154,8 @@ impl WriteBytes for PropNode { KEY_DOC_LINK_STR, data.doc_link.as_ref().map(|l| l.as_str()).unwrap_or(""), )?; + + write_key_value_line_opt(writer, KEY_DOCUMENTATION_STR, data.documentation.as_ref())?; } if let Some(unique_id) = match &self { @@ -213,6 +217,8 @@ impl ReadBytes for PropNode { Some(Url::parse(&doc_link_str).map_err(GraphError::parse)?) }; + let documentation = read_key_value_line_opt(reader, KEY_DOCUMENTATION_STR)?; + Some(PropNodeData { name: name.to_owned(), func_unique_id, @@ -221,6 +227,7 @@ impl ReadBytes for PropNode { widget_options, doc_link, hidden, + documentation, }) } }; @@ -261,7 +268,7 @@ impl ReadBytes for PropNode { invalid_kind => { return Err(GraphError::parse_custom(format!( "invalid prop node kind: {invalid_kind}" - ))) + ))); } }; @@ -317,6 +324,7 @@ impl NodeChild for PropSpec { widget_options, hidden, doc_link, + documentation, .. }| PropNodeData { name, @@ -326,6 +334,7 @@ impl NodeChild for PropSpec { widget_options, hidden: hidden.unwrap_or(false), doc_link, + documentation, }, ), unique_id.to_owned(), diff --git a/lib/si-pkg/src/pkg/prop.rs b/lib/si-pkg/src/pkg/prop.rs index 93efd322e0..43cdf229c0 100644 --- a/lib/si-pkg/src/pkg/prop.rs +++ b/lib/si-pkg/src/pkg/prop.rs @@ -18,6 +18,7 @@ pub struct SiPkgPropData { pub widget_options: Option, pub doc_link: Option, pub hidden: bool, + pub documentation: Option, } #[remain::sorted] @@ -121,7 +122,7 @@ impl<'a> SiPkgProp<'a> { return Err(SiPkgError::UnexpectedPkgNodeType( PkgNode::PROP_KIND_STR, unexpected.node_kind_str(), - )) + )); } }; @@ -169,6 +170,7 @@ impl<'a> SiPkgProp<'a> { widget_options, hidden, doc_link, + documentation, }| SiPkgPropData { name, default_value, @@ -177,6 +179,7 @@ impl<'a> SiPkgProp<'a> { widget_options, hidden, doc_link, + documentation, }, ), unique_id.to_owned(), diff --git a/lib/si-pkg/src/spec/prop.rs b/lib/si-pkg/src/spec/prop.rs index ce61890d62..9f9ac02641 100644 --- a/lib/si-pkg/src/spec/prop.rs +++ b/lib/si-pkg/src/spec/prop.rs @@ -59,6 +59,7 @@ pub struct PropSpecData { pub widget_options: Option, pub hidden: Option, pub doc_link: Option, + pub documentation: Option, } #[remain::sorted] @@ -128,6 +129,7 @@ pub enum PropSpecKind { pub struct PropSpecBuilder { default_value: Option, doc_link: Option, + documentation: Option, entries: Vec, func_unique_id: Option, hidden: bool, @@ -148,6 +150,7 @@ impl Default for PropSpecBuilder { Self { default_value: None, doc_link: None, + documentation: None, entries: vec![], func_unique_id: None, hidden: false, @@ -248,6 +251,11 @@ impl PropSpecBuilder { self } + pub fn documentation(&mut self, value: impl Into) -> &mut Self { + self.documentation = Some(value.into()); + self + } + pub fn map_key_func(&mut self, value: impl Into) -> &mut Self { self.has_data = true; self.map_key_funcs.push(value.into()); @@ -293,6 +301,7 @@ impl PropSpecBuilder { let widget_options = self.widget_options.to_owned(); let hidden = self.hidden; let doc_link = self.doc_link.to_owned(); + let documentation = self.documentation.to_owned(); Ok(match self.kind { Some(kind) => match kind { @@ -310,6 +319,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None @@ -329,6 +339,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None @@ -348,6 +359,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None @@ -367,6 +379,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None @@ -393,6 +406,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None @@ -418,6 +432,7 @@ impl PropSpecBuilder { widget_options, hidden: Some(hidden), doc_link, + documentation, }) } else { None diff --git a/lib/si-pkg/src/spec/variant.rs b/lib/si-pkg/src/spec/variant.rs index 293acbffa2..bf8d8cb70e 100644 --- a/lib/si-pkg/src/spec/variant.rs +++ b/lib/si-pkg/src/spec/variant.rs @@ -172,6 +172,7 @@ impl SchemaVariantSpecBuilder { hidden: Some(false), validations: None, doc_link: None, + documentation: None, }), entries: vec![], } @@ -191,6 +192,7 @@ impl SchemaVariantSpecBuilder { hidden: Some(false), validations: None, doc_link: None, + documentation: None, }), entries: vec![], } @@ -210,6 +212,7 @@ impl SchemaVariantSpecBuilder { hidden: Some(false), validations: None, doc_link: None, + documentation: None, }), entries: vec![], }) @@ -229,6 +232,7 @@ impl SchemaVariantSpecBuilder { hidden: Some(false), validations: None, doc_link: None, + documentation: None, }), entries: vec![], }