Skip to content

Commit f32561d

Browse files
authored
Merge branch 'main' into dlvhdr/devbox-and-widget-blueprint
2 parents 19a4238 + c949524 commit f32561d

File tree

8 files changed

+413
-30
lines changed

8 files changed

+413
-30
lines changed

docs/resources/port_action.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ Optional:
506506
- `order_properties` (List of String) Order properties
507507
- `required_jq_query` (String) The required jq query of the property
508508
- `steps` (Attributes List) The steps of the action (see [below for nested schema](#nestedatt--self_service_trigger--steps))
509+
- `titles` (Attributes Map) action titles (see [below for nested schema](#nestedatt--self_service_trigger--titles))
509510
- `user_properties` (Attributes) User properties (see [below for nested schema](#nestedatt--self_service_trigger--user_properties))
510511

511512
<a id="nestedatt--self_service_trigger--steps"></a>
@@ -517,6 +518,20 @@ Required:
517518
- `title` (String) The step's title (max 25 characters)
518519

519520

521+
<a id="nestedatt--self_service_trigger--titles"></a>
522+
### Nested Schema for `self_service_trigger.titles`
523+
524+
Required:
525+
526+
- `title` (String) The title of the action title
527+
528+
Optional:
529+
530+
- `description` (String) The description of the action title
531+
- `visible` (Boolean) The visibility of the string property
532+
- `visible_jq_query` (String) The visibility condition jq query of the string property
533+
534+
520535
<a id="nestedatt--self_service_trigger--user_properties"></a>
521536
### Nested Schema for `self_service_trigger.user_properties`
522537

examples/resources/port_action/main.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ resource "port_action" "restart_microservice" {
7979
self_service_trigger = {
8080
operation = "DAY-2"
8181
blueprint_identifier = port_blueprint.microservice.identifier
82+
titles = {
83+
"titleIdentifier" = {
84+
title = "My String Title"
85+
description = "My String Description",
86+
visible_jq_query = "true"
87+
}
88+
}
89+
order_properties = ["titleIdentifier","webhook_url","service","testString","testNumber"]
8290
user_properties = {
8391
string_props = {
8492
"webhook_url" = {

internal/cli/models.go

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package cli
22

33
import (
4+
"encoding/json"
5+
"reflect"
6+
"strings"
47
"time"
58

69
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -45,26 +48,28 @@ type (
4548
}
4649

4750
BlueprintProperty struct {
48-
Type string `json:"type,omitempty"`
49-
Title *string `json:"title,omitempty"`
50-
Identifier string `json:"identifier,omitempty"`
51-
Items map[string]any `json:"items,omitempty"`
52-
Default any `json:"default,omitempty"`
53-
Icon *string `json:"icon,omitempty"`
54-
Format *string `json:"format,omitempty"`
55-
MaxLength *int `json:"maxLength,omitempty"`
56-
MinLength *int `json:"minLength,omitempty"`
57-
MaxItems *int `json:"maxItems,omitempty"`
58-
MinItems *int `json:"minItems,omitempty"`
59-
Maximum *float64 `json:"maximum,omitempty"`
60-
Minimum *float64 `json:"minimum,omitempty"`
61-
Description *string `json:"description,omitempty"`
62-
Blueprint *string `json:"blueprint,omitempty"`
63-
Pattern *string `json:"pattern,omitempty"`
64-
Enum []any `json:"enum,omitempty"`
65-
Spec *string `json:"spec,omitempty"`
66-
SpecAuthentication *SpecAuthentication `json:"specAuthentication,omitempty"`
67-
EnumColors map[string]string `json:"enumColors,omitempty"`
51+
Type string `json:"type,omitempty"`
52+
Title *string `json:"title,omitempty"`
53+
Identifier string `json:"identifier,omitempty"`
54+
Items map[string]any `json:"items,omitempty"`
55+
Default any `json:"default,omitempty"`
56+
Icon *string `json:"icon,omitempty"`
57+
Format *string `json:"format,omitempty"`
58+
MaxLength *int `json:"maxLength,omitempty"`
59+
MinLength *int `json:"minLength,omitempty"`
60+
MaxItems *int `json:"maxItems,omitempty"`
61+
MinItems *int `json:"minItems,omitempty"`
62+
Maximum *float64 `json:"maximum,omitempty"`
63+
Minimum *float64 `json:"minimum,omitempty"`
64+
Description *string `json:"description,omitempty"`
65+
Blueprint *string `json:"blueprint,omitempty"`
66+
Pattern *string `json:"pattern,omitempty"`
67+
Enum []any `json:"enum,omitempty"`
68+
Spec *string `json:"spec,omitempty"`
69+
SpecAuthentication *SpecAuthentication `json:"specAuthentication,omitempty"`
70+
EnumColors map[string]string `json:"enumColors,omitempty"`
71+
// UnknownFields captures any dynamic fields not explicitly defined above
72+
UnknownFields map[string]any `json:"-"`
6873
}
6974

7075
EntitiesSortModel struct {
@@ -100,6 +105,12 @@ type (
100105
Sort *EntitiesSortModel `json:"sort,omitempty"`
101106
}
102107

108+
ActionTitle struct {
109+
Title string `json:"title"`
110+
Description *string `json:"description,omitempty"`
111+
Visible any `json:"visible,omitempty"`
112+
}
113+
103114
SpecAuthentication struct {
104115
ClientId string `json:"clientId,omitempty"`
105116
AuthorizationUrl string `json:"authorizationUrl,omitempty"`
@@ -218,6 +229,7 @@ type (
218229
Required any `json:"required,omitempty"`
219230
Order []string `json:"order,omitempty"`
220231
Steps []Step `json:"steps,omitempty"`
232+
Titles map[string]ActionTitle `json:"titles,omitempty"`
221233
}
222234

223235
TriggerEvent struct {
@@ -486,6 +498,94 @@ type (
486498
}
487499
)
488500

501+
// getKnownFields uses reflection to extract JSON field names from BlueprintProperty struct
502+
func getKnownFields(bp *BlueprintProperty) map[string]bool {
503+
knownFields := make(map[string]bool)
504+
t := reflect.TypeOf(*bp)
505+
506+
for i := 0; i < t.NumField(); i++ {
507+
field := t.Field(i)
508+
509+
// Get the JSON tag
510+
jsonTag := field.Tag.Get("json")
511+
if jsonTag == "" || jsonTag == "-" {
512+
continue // Skip fields without JSON tags or with "-"
513+
}
514+
515+
// Handle "fieldname,omitempty" format
516+
fieldName, _, _ := strings.Cut(jsonTag, ",")
517+
if fieldName != "" {
518+
knownFields[fieldName] = true
519+
}
520+
}
521+
522+
return knownFields
523+
}
524+
525+
// Custom UnmarshalJSON for BlueprintProperty to capture dynamic fields
526+
func (bp *BlueprintProperty) UnmarshalJSON(data []byte) error {
527+
// Define an alias to avoid infinite recursion
528+
type Alias BlueprintProperty
529+
530+
// First, unmarshal into the alias to populate known fields
531+
aux := &struct {
532+
*Alias
533+
}{
534+
Alias: (*Alias)(bp),
535+
}
536+
537+
if err := json.Unmarshal(data, aux); err != nil {
538+
return err
539+
}
540+
541+
// Now unmarshal into a map to capture all fields
542+
var all map[string]any
543+
if err := json.Unmarshal(data, &all); err != nil {
544+
return err
545+
}
546+
547+
// Initialize UnknownFields map
548+
bp.UnknownFields = make(map[string]any)
549+
550+
// Use reflection to get known fields instead of hardcoding
551+
knownFields := getKnownFields(bp)
552+
553+
// Add any unknown fields to UnknownFields
554+
for key, value := range all {
555+
if !knownFields[key] {
556+
bp.UnknownFields[key] = value
557+
}
558+
}
559+
560+
return nil
561+
}
562+
563+
// Custom MarshalJSON for BlueprintProperty to include dynamic fields
564+
func (bp BlueprintProperty) MarshalJSON() ([]byte, error) {
565+
// Define an alias to avoid infinite recursion
566+
type Alias BlueprintProperty
567+
568+
// Marshal the known fields first
569+
aux := Alias(bp)
570+
aux.UnknownFields = nil // Don't marshal this field directly
571+
572+
data, err := json.Marshal(aux)
573+
if err != nil {
574+
return nil, err
575+
}
576+
577+
var result map[string]any
578+
if err := json.Unmarshal(data, &result); err != nil {
579+
return nil, err
580+
}
581+
582+
for key, value := range bp.UnknownFields {
583+
result[key] = value
584+
}
585+
586+
return json.Marshal(result)
587+
}
588+
489589
type PortBody struct {
490590
OK bool `json:"ok"`
491591
Entity Entity `json:"entity"`

port/action/actionStateToPortBody.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func actionDataSetToPortBody(dataSet *DatasetModel) *cli.Dataset {
1919
dataSetRule := cli.DatasetRule{
2020
Operator: rule.Operator.ValueString(),
2121
}
22-
22+
2323
if rule.Value != nil && !rule.Value.JqQuery.IsNull() {
2424
dataSetRule.Value = &cli.DatasetValue{
2525
JqQuery: rule.Value.JqQuery.ValueString(),
@@ -100,6 +100,13 @@ func triggerToBody(ctx context.Context, data *ActionModel) (*cli.Trigger, error)
100100
}
101101
}
102102

103+
if data.SelfServiceTrigger.Titles != nil {
104+
err := actionTitlesToBody(ctx, selfServiceTrigger, data.SelfServiceTrigger)
105+
if err != nil {
106+
return nil, err
107+
}
108+
}
109+
103110
if !data.SelfServiceTrigger.OrderProperties.IsNull() {
104111
order, err := utils.TerraformListToGoArray(ctx, data.SelfServiceTrigger.OrderProperties, "string")
105112
if err != nil {
@@ -253,6 +260,41 @@ func actionPropertiesToBody(ctx context.Context, actionTrigger *cli.Trigger, dat
253260
return nil
254261
}
255262

263+
func actionTitlesToBody(ctx context.Context, actionTrigger *cli.Trigger, data *SelfServiceTriggerModel) error {
264+
actionTitles := map[string]cli.ActionTitle{}
265+
var err error
266+
if data.Titles != nil {
267+
for key, actionTitle := range data.Titles {
268+
269+
cliTitle := cli.ActionTitle{
270+
Title: actionTitle.Title.ValueString(),
271+
Description: actionTitle.Description.ValueStringPointer(),
272+
}
273+
274+
if !actionTitle.Visible.IsNull() {
275+
cliTitle.Visible = actionTitle.Visible.ValueBool()
276+
}
277+
278+
if !actionTitle.VisibleJqQuery.IsNull() {
279+
VisibleJqQueryMap := map[string]string{
280+
"jqQuery": actionTitle.VisibleJqQuery.ValueString(),
281+
}
282+
cliTitle.Visible = VisibleJqQueryMap
283+
}
284+
285+
actionTitles[key] = cliTitle
286+
}
287+
}
288+
289+
if err != nil {
290+
return err
291+
}
292+
293+
actionTrigger.UserInputs.Titles = actionTitles
294+
295+
return nil
296+
}
297+
256298
func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.InvocationMethod, error) {
257299
if data.KafkaMethod != nil {
258300
payload, err := utils.TerraformStringToGoType[interface{}](data.KafkaMethod.Payload)

port/action/model.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,22 @@ type Step struct {
284284
Order []types.String `tfsdk:"order"`
285285
}
286286

287+
type ActionTitle struct {
288+
Title types.String `tfsdk:"title"`
289+
Description types.String `tfsdk:"description"`
290+
Visible types.Bool `tfsdk:"visible"`
291+
VisibleJqQuery types.String `tfsdk:"visible_jq_query"`
292+
}
293+
287294
type SelfServiceTriggerModel struct {
288-
BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"`
289-
Operation types.String `tfsdk:"operation"`
290-
UserProperties *UserPropertiesModel `tfsdk:"user_properties"`
291-
RequiredJqQuery types.String `tfsdk:"required_jq_query"`
292-
OrderProperties types.List `tfsdk:"order_properties"`
293-
Steps []Step `tfsdk:"steps"`
294-
Condition types.String `tfsdk:"condition"`
295+
BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"`
296+
Operation types.String `tfsdk:"operation"`
297+
UserProperties *UserPropertiesModel `tfsdk:"user_properties"`
298+
Titles map[string]ActionTitle `tfsdk:"titles"`
299+
RequiredJqQuery types.String `tfsdk:"required_jq_query"`
300+
OrderProperties types.List `tfsdk:"order_properties"`
301+
Steps []Step `tfsdk:"steps"`
302+
Condition types.String `tfsdk:"condition"`
295303
}
296304

297305
type EntityCreatedEventModel struct {

port/action/refreshActionState.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,13 @@ func writeDatasetToResource(ds *cli.Dataset) *DatasetModel {
153153
Property: flex.GoStringToFramework(v.Property),
154154
Operator: flex.GoStringToFramework(&v.Operator),
155155
}
156-
156+
157157
if v.Value != nil {
158158
rule.Value = &Value{
159159
JqQuery: flex.GoStringToFramework(&v.Value.JqQuery),
160160
}
161161
}
162-
162+
163163
datasetModel.Rules = append(datasetModel.Rules, *rule)
164164
}
165165

@@ -325,18 +325,54 @@ func (r *ActionResource) buildUserProperties(ctx context.Context, a *cli.Action,
325325
return properties, nil
326326
}
327327

328+
func (r *ActionResource) buildActionTitles(a *cli.Action) (map[string]ActionTitle, error) {
329+
if a.Trigger.UserInputs.Titles == nil {
330+
return nil, nil
331+
}
332+
333+
actionTitles := make(map[string]ActionTitle)
334+
335+
for key, actionTitle := range a.Trigger.UserInputs.Titles {
336+
stateTitle := ActionTitle{
337+
Title: types.StringValue(actionTitle.Title),
338+
Description: flex.GoStringToFramework(actionTitle.Description),
339+
}
340+
341+
if actionTitle.Visible != nil {
342+
visible := reflect.ValueOf(actionTitle.Visible)
343+
switch visible.Kind() {
344+
case reflect.Bool:
345+
boolValue := visible.Interface().(bool)
346+
stateTitle.Visible = types.BoolValue(boolValue)
347+
case reflect.Map:
348+
jq := visible.Interface().(map[string]any)
349+
jqQueryValue := jq["jqQuery"].(string)
350+
stateTitle.VisibleJqQuery = types.StringValue(jqQueryValue)
351+
}
352+
}
353+
354+
actionTitles[key] = stateTitle
355+
}
356+
return actionTitles, nil
357+
}
358+
328359
func (r *ActionResource) writeTriggerToResource(ctx context.Context, a *cli.Action, state *ActionModel) error {
329360
if a.Trigger.Type == consts.SelfService {
330361
userProperties, err := r.buildUserProperties(ctx, a, state)
331362
if err != nil {
332363
return err
333364
}
365+
actionTitles, err := r.buildActionTitles(a)
366+
if err != nil {
367+
return err
368+
}
334369
requiredJqQuery, _ := buildRequired(a.Trigger.UserInputs)
335370
state.SelfServiceTrigger = &SelfServiceTriggerModel{
336371
BlueprintIdentifier: flex.GoStringToFramework(a.Trigger.BlueprintIdentifier),
337372
Operation: types.StringValue(*a.Trigger.Operation),
338373
UserProperties: userProperties,
339374
RequiredJqQuery: requiredJqQuery,
375+
Titles: actionTitles,
340376
}
341377

342378
if len(a.Trigger.UserInputs.Order) > 0 {

0 commit comments

Comments
 (0)