github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/steampipeconfig/parse/validate.go (about)

     1  package parse
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/hashicorp/hcl/v2"
     6  	"github.com/turbot/steampipe/pkg/steampipeconfig/modconfig"
     7  )
     8  
     9  // validate the resource
    10  func validateResource(resource modconfig.HclResource) hcl.Diagnostics {
    11  	var diags hcl.Diagnostics
    12  	if qp, ok := resource.(modconfig.NodeAndEdgeProvider); ok {
    13  		moreDiags := validateNodeAndEdgeProvider(qp)
    14  		diags = append(diags, moreDiags...)
    15  	} else if qp, ok := resource.(modconfig.QueryProvider); ok {
    16  		moreDiags := validateQueryProvider(qp)
    17  		diags = append(diags, moreDiags...)
    18  	}
    19  
    20  	if wp, ok := resource.(modconfig.WithProvider); ok {
    21  		moreDiags := validateRuntimeDependencyProvider(wp)
    22  		diags = append(diags, moreDiags...)
    23  	}
    24  	return diags
    25  }
    26  
    27  func validateRuntimeDependencyProvider(wp modconfig.WithProvider) hcl.Diagnostics {
    28  	resource := wp.(modconfig.HclResource)
    29  	var diags hcl.Diagnostics
    30  	if len(wp.GetWiths()) > 0 && !resource.IsTopLevel() {
    31  		diags = append(diags, &hcl.Diagnostic{
    32  			Severity: hcl.DiagError,
    33  			Summary:  "Only top level resources can have `with` blocks",
    34  			Detail:   fmt.Sprintf("%s contains 'with' blocks but is not a top level resource.", resource.Name()),
    35  			Subject:  resource.GetDeclRange(),
    36  		})
    37  	}
    38  	return diags
    39  }
    40  
    41  // validate that the provider does not contains both edges/nodes and a query/sql
    42  // enrich the loaded nodes and edges with the fully parsed resources from the resourceMapProvider
    43  func validateNodeAndEdgeProvider(resource modconfig.NodeAndEdgeProvider) hcl.Diagnostics {
    44  	// TODO [node_reuse] add NodeAndEdgeProviderImpl and move validate there
    45  	// https://github.com/turbot/steampipe/issues/2918
    46  
    47  	var diags hcl.Diagnostics
    48  	containsEdgesOrNodes := len(resource.GetEdges())+len(resource.GetNodes()) > 0
    49  	definesQuery := resource.GetSQL() != nil || resource.GetQuery() != nil
    50  
    51  	// cannot declare both edges/nodes AND sql/query
    52  	if definesQuery && containsEdgesOrNodes {
    53  		diags = append(diags, &hcl.Diagnostic{
    54  			Severity: hcl.DiagError,
    55  			Summary:  fmt.Sprintf("%s contains edges/nodes AND has a query", resource.Name()),
    56  			Subject:  resource.GetDeclRange(),
    57  		})
    58  	}
    59  
    60  	// if resource is NOT top level must have either edges/nodes OR sql/query
    61  	if !resource.IsTopLevel() && !definesQuery && !containsEdgesOrNodes {
    62  		diags = append(diags, &hcl.Diagnostic{
    63  			Severity: hcl.DiagError,
    64  			Summary:  fmt.Sprintf("%s does not define a query or SQL, and has no edges/nodes", resource.Name()),
    65  			Subject:  resource.GetDeclRange(),
    66  		})
    67  	}
    68  
    69  	diags = append(diags, validateSqlAndQueryNotBothSet(resource)...)
    70  
    71  	diags = append(diags, validateParamAndQueryNotBothSet(resource)...)
    72  
    73  	return diags
    74  }
    75  
    76  func validateQueryProvider(resource modconfig.QueryProvider) hcl.Diagnostics {
    77  	var diags hcl.Diagnostics
    78  
    79  	diags = append(diags, resource.ValidateQuery()...)
    80  
    81  	diags = append(diags, validateSqlAndQueryNotBothSet(resource)...)
    82  
    83  	diags = append(diags, validateParamAndQueryNotBothSet(resource)...)
    84  
    85  	return diags
    86  }
    87  
    88  func validateParamAndQueryNotBothSet(resource modconfig.QueryProvider) hcl.Diagnostics {
    89  	var diags hcl.Diagnostics
    90  
    91  	// param block cannot be set if a query property is set - it is only valid if inline SQL ids defined
    92  	if len(resource.GetParams()) > 0 {
    93  		if resource.GetQuery() != nil {
    94  			diags = append(diags, &hcl.Diagnostic{
    95  				Severity: hcl.DiagWarning,
    96  				Summary:  fmt.Sprintf("Deprecated usage: %s has 'query' property set so should not define 'param' blocks", resource.Name()),
    97  				Subject:  resource.GetDeclRange(),
    98  			})
    99  		}
   100  		if !resource.IsTopLevel() && !resource.ParamsInheritedFromBase() {
   101  			diags = append(diags, &hcl.Diagnostic{
   102  				Severity: hcl.DiagWarning,
   103  				Summary:  "Deprecated usage: Only top level resources can have 'param' blocks",
   104  				Detail:   fmt.Sprintf("%s contains 'param' blocks but is not a top level resource.", resource.Name()),
   105  				Subject:  resource.GetDeclRange(),
   106  			})
   107  		}
   108  	}
   109  	return diags
   110  }
   111  
   112  func validateSqlAndQueryNotBothSet(resource modconfig.QueryProvider) hcl.Diagnostics {
   113  	var diags hcl.Diagnostics
   114  	// are both sql and query set?
   115  	if resource.GetSQL() != nil && resource.GetQuery() != nil {
   116  		// either Query or SQL property may be set -  if Query property already set, error
   117  		diags = append(diags, &hcl.Diagnostic{
   118  			Severity: hcl.DiagError,
   119  			Summary:  fmt.Sprintf("%s has both 'SQL' and 'query' property set - only 1 of these may be set", resource.Name()),
   120  			Subject:  resource.GetDeclRange(),
   121  		})
   122  	}
   123  	return diags
   124  }