Repository: klothoplatform/klotho (go)

Issue:
Topology and config type are not in sync
Our topology uses the cloudResource.Type() which is never synced with the type in config. This is done for exec units by explicitly setting the type in code, but we should probably have a better solution than hardcoding it

Pull request:
making topology use config type and adding alb to topology
• Does any part of it require special attention?
• Does it relate to or fix any issue?

closes #111 

topology will get type from config since that is the source of truth. Added alb to the topology diagrams and validated it works.

Some small infra changes to the ALB code as well, will change some integ tests to use ALB so we can properly test it

We now remove Type() from all Cloud Resources so we have a single source of truth. This required me to bundle in the telemetry changes as well requested by ala since we report type up to datadog/mixpanel

### Standard checks

- **Unit tests**: Any special considerations?
- **Docs**: Do we need to update any docs, internal or public?
- **Backwards compatibility**: Will this break existing apps? If so, what would be the extra work required to keep them working?


Patch:
diff --git a/pkg/cli/helpers.go b/pkg/cli/helpers.go
index a8c11209..b0e8e80f 100644
--- a/pkg/cli/helpers.go
+++ b/pkg/cli/helpers.go
@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 	"strings"
 
+	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	execunit "github.com/klothoplatform/klotho/pkg/exec_unit"
 	"github.com/klothoplatform/klotho/pkg/infra/kubernetes"
@@ -167,24 +168,23 @@ func OutputResources(result *core.CompilationResult, outDir string) (resourceCou
 	return
 }
 
-func GetLanguagesUsed(result *core.CompilationResult) map[core.ExecutableType]bool {
-	executableLangs := make(map[core.ExecutableType]bool)
+func GetLanguagesUsed(result *core.CompilationResult) []core.ExecutableType {
+	executableLangs := []core.ExecutableType{}
 	for _, res := range result.Resources() {
 		switch r := res.(type) {
 		case *core.ExecutionUnit:
-			executableLangs[r.Executable.Type] = true
+			executableLangs = append(executableLangs, r.Executable.Type)
 		}
 	}
 	return executableLangs
 }
 
-func GetResourceTypeCount(result *core.CompilationResult) (resourceCounts map[string]map[string]int) {
-	resourceCounts = make(map[string]map[string]int)
+func GetResourceTypeCount(result *core.CompilationResult, cfg *config.Application) (resourceCounts []string) {
 	for _, res := range result.Resources() {
-		if _, ok := resourceCounts[res.Key().Kind]; !ok {
-			resourceCounts[res.Key().Kind] = make(map[string]int)
+		resType := cfg.GetResourceType(res)
+		if resType != "" {
+			resourceCounts = append(resourceCounts, resType)
 		}
-		resourceCounts[res.Key().Kind][res.Type()]++
 	}
 	return
 }
diff --git a/pkg/cli/klothomain.go b/pkg/cli/klothomain.go
index 96921a02..1bb7e12f 100644
--- a/pkg/cli/klothomain.go
+++ b/pkg/cli/klothomain.go
@@ -218,7 +218,7 @@ func (km KlothoMain) run(cmd *cobra.Command, args []string) (err error) {
 	}
 	klothoName := "klotho"
 	if km.VersionQualifier != "" {
-		klothoName += " " + km.VersionQualifier
+		analyticsClient.Properties[km.VersionQualifier] = true
 	}
 
 	// if update is specified do the update in place
@@ -290,7 +290,7 @@ func (km KlothoMain) run(cmd *cobra.Command, args []string) (err error) {
 		"app":      appCfg.AppName,
 	})
 
-	analyticsClient.Info(klothoName + "pre-compile")
+	analyticsClient.Info(klothoName + " pre-compile")
 
 	input, err := input.ReadOSDir(appCfg, cfg.config)
 	if err != nil {
@@ -321,7 +321,7 @@ func (km KlothoMain) run(cmd *cobra.Command, args []string) (err error) {
 		Plugins: plugins.Plugins(),
 	}
 
-	analyticsClient.Info(klothoName + "compiling")
+	analyticsClient.Info(klothoName + " compiling")
 
 	result, err := compiler.Compile(input)
 	if err != nil || hadErrors.Load() {
@@ -330,7 +330,7 @@ func (km KlothoMain) run(cmd *cobra.Command, args []string) (err error) {
 		} else {
 			err = errors.New("Failed run of klotho invocation")
 		}
-		analyticsClient.Error(klothoName + "compiling failed")
+		analyticsClient.Error(klothoName + " compiling failed")
 
 		cmd.SilenceErrors = true
 		cmd.SilenceUsage = true
@@ -347,10 +347,10 @@ func (km KlothoMain) run(cmd *cobra.Command, args []string) (err error) {
 	}
 
 	CloseTreeSitter(result)
-	analyticsClient.AppendProperties(map[string]interface{}{"resource_types": GetResourceTypeCount(result)})
+	analyticsClient.AppendProperties(map[string]interface{}{"resource_types": GetResourceTypeCount(result, &appCfg)})
 	analyticsClient.AppendProperties(map[string]interface{}{"languages": GetLanguagesUsed(result)})
 	analyticsClient.AppendProperties(map[string]interface{}{"resources": resourceCounts})
-	analyticsClient.Info(klothoName + "compile complete")
+	analyticsClient.Info(klothoName + " compile complete")
 
 	return nil
 }
diff --git a/pkg/core/compiler.go b/pkg/core/compiler.go
index e5ccfc4c..0af95e9e 100644
--- a/pkg/core/compiler.go
+++ b/pkg/core/compiler.go
@@ -18,8 +18,6 @@ type (
 	}
 
 	CloudResource interface {
-		// Type returns the provider-specific type (implementation) of the resource's kind (`Key().Kind`)
-		Type() string
 		// Key returns the unique identifier for the resource.
 		Key() ResourceKey
 	}
diff --git a/pkg/core/exec_unit.go b/pkg/core/exec_unit.go
index 9c563d9e..3f9dca4b 100644
--- a/pkg/core/exec_unit.go
+++ b/pkg/core/exec_unit.go
@@ -15,7 +15,6 @@ import (
 
 type (
 	ExecutionUnit struct {
-		ExecType             string
 		Name                 string
 		files                ConcurrentMap[string, File]
 		Executable           Executable
@@ -74,8 +73,6 @@ var (
 	ExecutableTypeGolang = ExecutableType("Golang")
 )
 
-func (unit *ExecutionUnit) Type() string { return unit.ExecType }
-
 func (unit *ExecutionUnit) Key() ResourceKey {
 	return ResourceKey{
 		Name: unit.Name,
diff --git a/pkg/core/gateway.go b/pkg/core/gateway.go
index 0d98d2d8..9c2398b5 100644
--- a/pkg/core/gateway.go
+++ b/pkg/core/gateway.go
@@ -3,7 +3,6 @@ package core
 type (
 	Gateway struct {
 		Name   string
-		GWType string
 		Routes []Route
 		// Map of gateway targets with the exec unit name as the key
 		DefinedIn     string
@@ -41,7 +40,6 @@ var (
 	}
 )
 
-func (gw *Gateway) Type() string { return gw.GWType }
 func NewGateway(name string) *Gateway {
 	return &Gateway{
 		Name: name,
diff --git a/pkg/core/infra_as_code.go b/pkg/core/infra_as_code.go
index a76f6e3d..dac2ac4d 100644
--- a/pkg/core/infra_as_code.go
+++ b/pkg/core/infra_as_code.go
@@ -12,8 +12,6 @@ type InfraFiles struct {
 
 var InfraAsCodeKind = "infra_as_code"
 
-func (*InfraFiles) Type() string { return "" }
-
 func (iac *InfraFiles) Key() ResourceKey {
 	return ResourceKey{
 		Name: iac.Name,
diff --git a/pkg/core/input.go b/pkg/core/input.go
index 120188f9..a36da129 100644
--- a/pkg/core/input.go
+++ b/pkg/core/input.go
@@ -6,8 +6,6 @@ type (
 
 var InputFilesKind = "input_files"
 
-func (*InputFiles) Type() string { return "" }
-
 func (*InputFiles) Key() ResourceKey {
 	return ResourceKey{
 		Kind: InputFilesKind,
diff --git a/pkg/core/persist.go b/pkg/core/persist.go
index 7d173755..7021b41f 100644
--- a/pkg/core/persist.go
+++ b/pkg/core/persist.go
@@ -7,9 +7,8 @@ import (
 
 type (
 	Persist struct {
-		Name        string
-		PersistType string
-		Kind        PersistKind
+		Name string
+		Kind PersistKind
 	}
 	Secrets struct {
 		Persist
@@ -28,8 +27,6 @@ const (
 	PersistRedisClusterKind PersistKind = "persist_redis_cluster"
 )
 
-func (p *Persist) Type() string { return p.PersistType }
-
 func (p *Persist) Key() ResourceKey {
 	return ResourceKey{
 		Name: p.Name,
diff --git a/pkg/core/pubsub.go b/pkg/core/pubsub.go
index 906eaba3..5839cfc7 100644
--- a/pkg/core/pubsub.go
+++ b/pkg/core/pubsub.go
@@ -4,7 +4,6 @@ type (
 	PubSub struct {
 		Path   string
 		Name   string
-		PSType string
 		Events map[string]*Event
 	}
 
@@ -22,8 +21,6 @@ type (
 
 const PubSubKind = "pubsub"
 
-func (p PubSub) Type() string { return p.PSType }
-
 func (p PubSub) Key() ResourceKey {
 	return ResourceKey{
 		Name: p.Name,
diff --git a/pkg/core/static_unit.go b/pkg/core/static_unit.go
index 16079142..50411030 100644
--- a/pkg/core/static_unit.go
+++ b/pkg/core/static_unit.go
@@ -7,7 +7,6 @@ import (
 
 type (
 	StaticUnit struct {
-		StaticType    string
 		Name          string
 		IndexDocument string
 		StaticFiles   ConcurrentMap[string, File]
@@ -17,8 +16,6 @@ type (
 
 var StaticUnitKind = "static_unit"
 
-func (unit *StaticUnit) Type() string { return unit.StaticType }
-
 func (unit *StaticUnit) Key() ResourceKey {
 	return ResourceKey{
 		Name: unit.Name,
diff --git a/pkg/core/topology.go b/pkg/core/topology.go
index 6af8d820..c5c6336e 100644
--- a/pkg/core/topology.go
+++ b/pkg/core/topology.go
@@ -62,7 +62,8 @@ var DiagramEntityToImgPath = TopoMap{
 	{Kind: PubSubKind}:                      "generic/blank/blank.png",
 
 	// Use AWS as the ultimate fallback for the Kind, so don't specify Provider.
-	{Kind: GatewayKind, Provider: ProviderAWS}:                          "aws/network/api-gateway.png",
+	{Kind: GatewayKind, Provider: ProviderAWS, Type: "apigateway"}:      "aws/network/api-gateway.png",
+	{Kind: GatewayKind, Provider: ProviderAWS, Type: "alb"}:             "aws/network/elb-application-load-balancer.png",
 	{Kind: ExecutionUnitKind, Provider: ProviderAWS}:                    "aws/compute/lambda.png",
 	{Kind: string(PersistKVKind), Provider: ProviderAWS}:                "aws/database/dynamodb.png",
 	{Kind: string(PersistFileKind), Provider: ProviderAWS}:              "aws/compute/simple-storage-service-s3.png",
@@ -109,7 +110,9 @@ var DiagramEntityToCode = TopoMap{
 	{Kind: PubSubKind}:                      `generic_blank.Blank("%s")`,
 
 	// Use AWS as the ultimate fallback for the Kind, so don't specify Provider.
-	{Kind: GatewayKind, Provider: ProviderAWS}:                     `aws_network.APIGateway("%s")`,
+	{Kind: GatewayKind, Provider: ProviderAWS, Type: "apigateway"}: `aws_network.APIGateway("%s")`,
+	{Kind: GatewayKind, Provider: ProviderAWS, Type: "alb"}:        `aws_network.ElbNetworkLoadBalancer("%s")`,
+
 	{Kind: ExecutionUnitKind, Provider: ProviderAWS}:               `aws_compute.Lambda("%s")`,
 	{Kind: string(PersistKVKind), Provider: ProviderAWS}:           `aws_database.Dynamodb("%s")`,
 	{Kind: string(PersistFileKind), Provider: ProviderAWS}:         `aws_storage.S3("%s")`,
diff --git a/pkg/exec_unit/plugin_exec_unit.go b/pkg/exec_unit/plugin_exec_unit.go
index 08bab824..c9fa4f8d 100644
--- a/pkg/exec_unit/plugin_exec_unit.go
+++ b/pkg/exec_unit/plugin_exec_unit.go
@@ -24,7 +24,6 @@ func (p ExecUnitPlugin) Transform(result *core.CompilationResult, deps *core.Dep
 		Executable: core.NewExecutable(),
 	}
 	cfg := p.Config.GetExecutionUnit(unit.Name)
-	unit.ExecType = cfg.Type
 
 	for key, value := range cfg.EnvironmentVariables {
 		unit.EnvironmentVariables = append(unit.EnvironmentVariables, core.EnvironmentVariable{
diff --git a/pkg/infra/pulumi_aws/iac/load_balancing.ts b/pkg/infra/pulumi_aws/iac/load_balancing.ts
index 08f09828..6eb59dc3 100644
--- a/pkg/infra/pulumi_aws/iac/load_balancing.ts
+++ b/pkg/infra/pulumi_aws/iac/load_balancing.ts
@@ -53,6 +53,19 @@ export class LoadBalancerPlugin {
         })
     }
 
+    private subPathParametersForWildcard(route: string): string {
+        const segments = route.split('/')
+        const newRoute: string[] = []
+        segments.forEach((seg) => {
+            if (seg.startsWith(':')) {
+                newRoute.push('*')
+            } else {
+                newRoute.push(seg)
+            }
+        })
+        return newRoute.join('/')
+    }
+
     private createALBasGateway = (gateway: Gateway): pulumi.Output<string> => {
         const albSG = new aws.ec2.SecurityGroup(`${this.lib.name}-${gateway.Name}`, {
             name: `${this.lib.name}-${gateway.Name}`,
@@ -118,14 +131,14 @@ export class LoadBalancerPlugin {
                             type: 'fixed-response',
                             fixedResponse: {
                                 contentType: 'application/json',
-                                statusCode: '4XX',
+                                statusCode: '404',
                             },
                         },
                     ],
                 })
             }
 
-            this.createListenerRule(this.lib.name, route.execUnitName + route.path, {
+            this.createListenerRule(this.lib.name, route.execUnitName + route.path + route.verb, {
                 listenerArn: listener!.arn,
                 actions: [
                     {
@@ -136,7 +149,15 @@ export class LoadBalancerPlugin {
                 conditions: [
                     {
                         pathPattern: {
-                            values: [route.path],
+                            values: [
+                                this.subPathParametersForWildcard(route.path),
+                                `${this.subPathParametersForWildcard(route.path)}/`,
+                            ],
+                        },
+                    },
+                    {
+                        httpRequestMethod: {
+                            values: [route.verb.toUpperCase()],
                         },
                     },
                 ],
diff --git a/pkg/lang/golang/aws_runtime/aws.go b/pkg/lang/golang/aws_runtime/aws.go
index 7ec66bc6..3166d5b9 100644
--- a/pkg/lang/golang/aws_runtime/aws.go
+++ b/pkg/lang/golang/aws_runtime/aws.go
@@ -34,11 +34,12 @@ var dockerfileLambda []byte
 
 func (r *AwsRuntime) AddExecRuntimeFiles(unit *core.ExecutionUnit, result *core.CompilationResult, deps *core.Dependencies) error {
 	var DockerFile []byte
-	switch unit.Type() {
+	unitType := r.Cfg.GetResourceType(unit)
+	switch unitType {
 	case "lambda":
 		DockerFile = dockerfileLambda
 	default:
-		return errors.Errorf("unsupported execution unit type: '%s'", unit.Type())
+		return errors.Errorf("unsupported execution unit type: '%s'", unitType)
 	}
 
 	templateData := TemplateData{
diff --git a/pkg/lang/golang/plugin_add_exec_runtime_files.go b/pkg/lang/golang/plugin_add_exec_runtime_files.go
index 486eda4f..bef77cf3 100644
--- a/pkg/lang/golang/plugin_add_exec_runtime_files.go
+++ b/pkg/lang/golang/plugin_add_exec_runtime_files.go
@@ -23,7 +23,6 @@ func (p *AddExecRuntimeFiles) Transform(result *core.CompilationResult, deps *co
 			continue
 		}
 
-		unit.ExecType = p.cfg.GetExecutionUnit(unit.Name).Type
 		errs.Append(p.runtime.AddExecRuntimeFiles(unit, result, deps))
 	}
 
diff --git a/pkg/lang/golang/plugin_expose.go b/pkg/lang/golang/plugin_expose.go
index 0e007f74..81ef1583 100644
--- a/pkg/lang/golang/plugin_expose.go
+++ b/pkg/lang/golang/plugin_expose.go
@@ -7,6 +7,7 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/logging"
 	"github.com/klothoplatform/klotho/pkg/multierr"
@@ -16,7 +17,9 @@ import (
 )
 
 type (
-	Expose struct{}
+	Expose struct {
+		Config *config.Application
+	}
 
 	gatewaySpec struct {
 		FilePath   string
@@ -80,14 +83,14 @@ func (p Expose) Transform(result *core.CompilationResult, deps *core.Dependencie
 
 func (p *Expose) transformSingle(result *core.CompilationResult, deps *core.Dependencies, unit *core.ExecutionUnit) error {
 	h := &restAPIHandler{Result: result, Deps: deps, RoutesByGateway: make(map[gatewaySpec][]gatewayRouteDefinition)}
-	err := h.handle(unit)
+	err := h.handle(unit, p.Config.GetResourceType(unit))
 	if err != nil {
 		err = core.WrapErrf(err, "Chi handler failure for %s", unit.Name)
 	}
 	return err
 }
 
-func (h *restAPIHandler) handle(unit *core.ExecutionUnit) error {
+func (h *restAPIHandler) handle(unit *core.ExecutionUnit, unitType string) error {
 	h.Unit = unit
 	h.log = zap.L().With(zap.String("unit", unit.Name))
 
@@ -98,7 +101,7 @@ func (h *restAPIHandler) handle(unit *core.ExecutionUnit) error {
 			continue
 		}
 
-		newF, err := h.handleFile(src)
+		newF, err := h.handleFile(src, unitType)
 		if err != nil {
 			errs.Append(err)
 			continue
@@ -144,7 +147,7 @@ func (h *restAPIHandler) handle(unit *core.ExecutionUnit) error {
 	return errs.ErrOrNil()
 }
 
-func (h *restAPIHandler) handleFile(f *core.SourceFile) (*core.SourceFile, error) {
+func (h *restAPIHandler) handleFile(f *core.SourceFile, unitType string) (*core.SourceFile, error) {
 
 	caps := f.Annotations()
 	for _, capNode := range caps {
@@ -183,7 +186,7 @@ func (h *restAPIHandler) handleFile(f *core.SourceFile) (*core.SourceFile, error
 		}
 
 		//TODO: Move comment listen code to library logic like JS does eventually
-		if h.Unit.ExecType == "lambda" {
+		if unitType == "lambda" {
 			//TODO: Will likely need to move this into a separate plugin of some sort
 			// Instead of having a dispatcher file, the dipatcher logic is injected into the main.go file. By having that
 			// logic in the expose plugin though, it will only happen if they use the expose annotation for the lambda case.
diff --git a/pkg/lang/golang/plugins.go b/pkg/lang/golang/plugins.go
index 44d5b89c..4c69c39b 100644
--- a/pkg/lang/golang/plugins.go
+++ b/pkg/lang/golang/plugins.go
@@ -15,7 +15,7 @@ type (
 func NewGoPlugins(cfg *config.Application, runtime Runtime) *GoPlugins {
 	return &GoPlugins{
 		Plugins: []core.Plugin{
-			&Expose{},
+			&Expose{Config: cfg},
 			&AddExecRuntimeFiles{cfg: cfg, runtime: runtime},
 		},
 	}
diff --git a/pkg/lang/javascript/aws_runtime/aws.go b/pkg/lang/javascript/aws_runtime/aws.go
index b9d2d280..99dd7ae1 100644
--- a/pkg/lang/javascript/aws_runtime/aws.go
+++ b/pkg/lang/javascript/aws_runtime/aws.go
@@ -174,6 +174,7 @@ func (r *AwsRuntime) AddPubsubRuntimeFiles(unit *core.ExecutionUnit) error {
 
 func (r *AwsRuntime) AddProxyRuntimeFiles(unit *core.ExecutionUnit, proxyType string) error {
 	var proxyFile []byte
+	unitType := r.Config.GetResourceType(unit)
 	switch proxyType {
 	case "eks":
 		proxyFile = proxyEks
@@ -184,7 +185,7 @@ func (r *AwsRuntime) AddProxyRuntimeFiles(unit *core.ExecutionUnit, proxyType st
 	case "lambda":
 		proxyFile = proxyLambda
 	default:
-		return errors.Errorf("unsupported exceution unit type: '%s'", unit.Type())
+		return errors.Errorf("unsupported exceution unit type: '%s'", unitType)
 	}
 
 	err := r.AddRuntimeFile(unit, proxyType+"_proxy.js.tmpl", proxyFile)
@@ -201,7 +202,8 @@ func (r *AwsRuntime) AddProxyRuntimeFiles(unit *core.ExecutionUnit, proxyType st
 
 func (r *AwsRuntime) AddExecRuntimeFiles(unit *core.ExecutionUnit, result *core.CompilationResult, deps *core.Dependencies) error {
 	var DockerFile, Dispatcher []byte
-	switch unit.Type() {
+	unitType := r.Config.GetResourceType(unit)
+	switch unitType {
 	case "ecs", "eks", "apprunner":
 		DockerFile = dockerfileFargate
 		Dispatcher = dispatcherFargate
@@ -209,7 +211,7 @@ func (r *AwsRuntime) AddExecRuntimeFiles(unit *core.ExecutionUnit, result *core.
 		DockerFile = dockerfileLambda
 		Dispatcher = dispatcherLambda
 	default:
-		return errors.Errorf("unsupported execution unit type: '%s'", unit.Type())
+		return errors.Errorf("unsupported execution unit type: '%s'", unitType)
 	}
 
 	templateData := TemplateData{
diff --git a/pkg/lang/javascript/express.go b/pkg/lang/javascript/express.go
index 60e09dcf..c1352be7 100644
--- a/pkg/lang/javascript/express.go
+++ b/pkg/lang/javascript/express.go
@@ -8,6 +8,7 @@ import (
 	"strings"
 
 	"github.com/klothoplatform/klotho/pkg/annotation"
+	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/logging"
 	"github.com/klothoplatform/klotho/pkg/multierr"
@@ -20,6 +21,7 @@ import (
 type ExpressHandler struct {
 	output expressOutput
 	log    *zap.Logger
+	Config *config.Application
 }
 
 type expressMiddleware struct {
@@ -140,7 +142,7 @@ func (p *ExpressHandler) handleFile(f *core.SourceFile, unit *core.ExecutionUnit
 			return nil, core.NewCompilerError(f, annot, errors.New("Couldnt find expose app creation"))
 		}
 
-		actedOn, newfileContent := p.actOnAnnotation(f, &listen, fileContent, appName, unit.ExecType, cap.ID)
+		actedOn, newfileContent := p.actOnAnnotation(f, &listen, fileContent, appName, p.Config.GetResourceType(unit), cap.ID)
 		if actedOn {
 			fileContent = newfileContent
 			err := f.Reparse([]byte(fileContent))
diff --git a/pkg/lang/javascript/nestjs.go b/pkg/lang/javascript/nestjs.go
index a45f6252..bb347bae 100644
--- a/pkg/lang/javascript/nestjs.go
+++ b/pkg/lang/javascript/nestjs.go
@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 	"strings"
 
+	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/filter"
 	"github.com/klothoplatform/klotho/pkg/filter/predicate"
 
@@ -22,6 +23,7 @@ import (
 type NestJsHandler struct {
 	output nestJsOutput
 	log    *zap.Logger
+	Config *config.Application
 }
 
 type nestJsOutput struct {
@@ -122,7 +124,7 @@ func (p *NestJsHandler) handleFile(f *core.SourceFile, unit *core.ExecutionUnit)
 			return nil, core.NewCompilerError(f, annot, errors.New("Couldnt find expose app creation"))
 		}
 
-		actedOn, newfileContent := p.actOnAnnotation(f, &listen, fileContent, appName, unit.ExecType, cap.ID)
+		actedOn, newfileContent := p.actOnAnnotation(f, &listen, fileContent, appName, p.Config.GetResourceType(unit), cap.ID)
 		if actedOn {
 			fileContent = newfileContent
 			err := f.Reparse([]byte(fileContent))
diff --git a/pkg/lang/javascript/plugins.go b/pkg/lang/javascript/plugins.go
index bf5ff535..6bd795e7 100644
--- a/pkg/lang/javascript/plugins.go
+++ b/pkg/lang/javascript/plugins.go
@@ -15,8 +15,8 @@ type (
 func NewJavascriptPlugins(cfg *config.Application, runtime Runtime) *JavascriptPlugins {
 	return &JavascriptPlugins{
 		Plugins: []core.Plugin{
-			ExpressHandler{},
-			NestJsHandler{},
+			ExpressHandler{Config: cfg},
+			NestJsHandler{Config: cfg},
 			AddExecRuntimeFiles{runtime: runtime},
 			Persist{runtime: runtime},
 			Pubsub{runtime: runtime},
diff --git a/pkg/lang/python/aws_runtime/aws.go b/pkg/lang/python/aws_runtime/aws.go
index 07df30a9..1869d758 100644
--- a/pkg/lang/python/aws_runtime/aws.go
+++ b/pkg/lang/python/aws_runtime/aws.go
@@ -94,7 +94,8 @@ func (r *AwsRuntime) GetAppName() string {
 func (r *AwsRuntime) AddExecRuntimeFiles(unit *core.ExecutionUnit, result *core.CompilationResult, deps *core.Dependencies) error {
 	var dockerFile, dispatcher []byte
 	var requirements string
-	switch unit.Type() {
+	unitType := r.Cfg.GetResourceType(unit)
+	switch unitType {
 	case "lambda":
 		dockerFile = dockerfileLambda
 		dispatcher = dispatcherLambda
@@ -104,7 +105,7 @@ func (r *AwsRuntime) AddExecRuntimeFiles(unit *core.ExecutionUnit, result *core.
 		dispatcher = dispatcherFargate
 		requirements = execRequirementsFargate
 	default:
-		return errors.Errorf("unsupported execution unit type: '%s'", unit.Type())
+		return errors.Errorf("unsupported execution unit type: '%s'", unitType)
 	}
 
 	templateData := TemplateData{
@@ -242,7 +243,7 @@ func (r *AwsRuntime) AddProxyRuntimeFiles(unit *core.ExecutionUnit, proxyType st
 	case "lambda":
 		fileContents = proxyLambdaContents
 	default:
-		return errors.Errorf("unsupported exceution unit type: '%s'", unit.Type())
+		return errors.Errorf("unsupported exceution unit type: '%s'", r.Cfg.GetResourceType(unit))
 	}
 	err := r.AddRuntimeFile(unit, proxyType+"_proxy.py", []byte(fileContents))
 	if err != nil {
diff --git a/pkg/lang/python/plugin_add_exec_runtime_files.go b/pkg/lang/python/plugin_add_exec_runtime_files.go
index 42964c71..8766a78b 100644
--- a/pkg/lang/python/plugin_add_exec_runtime_files.go
+++ b/pkg/lang/python/plugin_add_exec_runtime_files.go
@@ -26,7 +26,6 @@ func (p *AddExecRuntimeFiles) Transform(result *core.CompilationResult, deps *co
 			continue
 		}
 
-		unit.ExecType = p.cfg.GetExecutionUnit(unit.Name).Type
 		errs.Append(p.runtime.AddExecRuntimeFiles(unit, result, deps))
 	}
 
diff --git a/pkg/logging/fields.go b/pkg/logging/fields.go
index 65475fe8..d586e7ad 100644
--- a/pkg/logging/fields.go
+++ b/pkg/logging/fields.go
@@ -18,9 +18,8 @@ type fileField struct {
 
 func (field fileField) Sanitize(hasher func(any) string) SanitizedField {
 	extension := "unknown"
-	switch ext := filepath.Ext(field.f.Path()); ext {
-	case ".json", ".js", ".ts", ".yaml":
-		extension = ext
+	if _, isFileRef := field.f.(*core.FileRef); !isFileRef {
+		extension = filepath.Ext(field.f.Path())
 	}
 	return SanitizedField{
 		Key: "FileExtension",
diff --git a/pkg/provider/aws/infra_template.go b/pkg/provider/aws/infra_template.go
index ee0c19f4..15637312 100644
--- a/pkg/provider/aws/infra_template.go
+++ b/pkg/provider/aws/infra_template.go
@@ -29,7 +29,7 @@ func (a *AWS) Transform(result *core.CompilationResult, deps *core.Dependencies)
 			cfg := a.Config.GetExecutionUnit(key.Name)
 			unit := provider.ExecUnit{
 				Name:                 res.Name,
-				Type:                 res.Type(),
+				Type:                 cfg.Type,
 				EnvironmentVariables: res.EnvironmentVariables,
 				NetworkPlacement:     cfg.NetworkPlacement,
 			}
@@ -91,7 +91,7 @@ func (a *AWS) Transform(result *core.CompilationResult, deps *core.Dependencies)
 			cfg := a.Config.GetStaticUnit(key.Name)
 			unit := provider.StaticUnit{
 				Name:                   res.Name,
-				Type:                   res.Type(),
+				Type:                   cfg.Type,
 				IndexDocument:          res.IndexDocument,
 				ContentDeliveryNetwork: cfg.ContentDeliveryNetwork,
 			}
diff --git a/pkg/static_unit/plugin_static_unit.go b/pkg/static_unit/plugin_static_unit.go
index 3ce31e27..98a19105 100644
--- a/pkg/static_unit/plugin_static_unit.go
+++ b/pkg/static_unit/plugin_static_unit.go
@@ -53,8 +53,7 @@ func (p StaticUnitSplit) Transform(result *core.CompilationResult, deps *core.De
 					errs.Append(cause)
 				}
 				newUnit := &core.StaticUnit{
-					StaticType: p.Config.GetStaticUnit(cap.ID).Type,
-					Name:       cap.ID,
+					Name: cap.ID,
 				}
 
 				indexDocument, ok := cap.Directives.String("index_document")
diff --git a/pkg/topology/topology.go b/pkg/topology/topology.go
index 261b5c20..23e5e3f8 100644
--- a/pkg/topology/topology.go
+++ b/pkg/topology/topology.go
@@ -66,7 +66,7 @@ func (p Plugin) generateIconsAndEdges(resource core.CloudResource, dependencies
 		Title: resource.Key().Name,
 		Image: p.getImagePath(resource),
 		Kind:  resource.Key().Kind,
-		Type:  resource.Type(),
+		Type:  p.Config.GetResourceType(resource),
 	})
 
 	for _, dependency := range dependencies {
@@ -84,7 +84,7 @@ func generateIconID(resource core.ResourceKey) string {
 	return fmt.Sprintf("%s_%s", resource.Name, resource.Kind)
 }
 func (p Plugin) getImagePath(resource core.CloudResource) string {
-	imgPath, _ := core.DiagramEntityToImgPath.Get(resource.Key().Kind, resource.Type(), p.Config.Provider)
+	imgPath, _ := core.DiagramEntityToImgPath.Get(resource.Key().Kind, p.Config.GetResourceType(resource), p.Config.Provider)
 	return baseIconUrl + imgPath
 }
 


Test patch:
diff --git a/.github/workflows/run-integ-tests.yaml b/.github/workflows/run-integ-tests.yaml
index 0ae573fe..680c6e75 100644
--- a/.github/workflows/run-integ-tests.yaml
+++ b/.github/workflows/run-integ-tests.yaml
@@ -60,6 +60,11 @@ on:
         required: false
         type: string
         default: us-east-2
+      pre-build-script: # This input is intentionally NOT in workflow_dispatch
+        description: path to a script (within the caller's repo) to invoke before `go build`. must be executable.
+        required: false
+        type: string
+        default: ''
 concurrency: integ-tests
 name: run integration tests
 jobs:
@@ -111,6 +116,9 @@ jobs:
       - uses: actions/setup-go@v3
         with:
           go-version: 1.19.x
+      - name: pre-build
+        if: ${{ inputs.pre-build-script }}
+        run: ${{ inputs.pre-build-script }}
       - name: build
         run: |
           go generate ./...
@@ -131,16 +139,6 @@ jobs:
       matrix:
         app_to_test: ${{ fromJson(needs.list-apps.outputs.to_test) }}
         mode: [fresh, upgrade]
-        exclude:
-          - # issue #48
-            app_to_test: ts-eks
-            mode: upgrade
-          - # issue #49
-            app_to_test: ts-eks-helm
-            mode: upgrade
-          - 
-            app_to_test: ts-events
-            mode: upgrade
     steps:
       - name: versions
         run: |
diff --git a/pkg/infra/kubernetes/persist_test.go b/pkg/infra/kubernetes/persist_test.go
index 942e5779..34d9078a 100644
--- a/pkg/infra/kubernetes/persist_test.go
+++ b/pkg/infra/kubernetes/persist_test.go
@@ -288,19 +288,19 @@ func Test_generateEnvVarsForPersist(t *testing.T) {
 	}{
 		{
 			name:     "Wrong dependencies",
-			unit:     &core.ExecutionUnit{Name: "main", ExecType: "exec_unit"},
+			unit:     &core.ExecutionUnit{Name: "main"},
 			resource: &core.Persist{Name: "file", Kind: core.PersistFileKind},
 			values:   []core.EnvironmentVariable{},
 		},
 		{
 			name:     "orm dependency",
-			unit:     &core.ExecutionUnit{Name: "main", ExecType: "exec_unit"},
+			unit:     &core.ExecutionUnit{Name: "main"},
 			resource: &core.Persist{Name: "orm", Kind: core.PersistORMKind},
 			values:   []core.EnvironmentVariable{{Name: "ORM_PERSIST_ORM_CONNECTION", Kind: "persist_orm", ResourceID: "orm", Value: string(core.CONNECTION_STRING)}},
 		},
 		{
 			name:     "redis node dependency",
-			unit:     &core.ExecutionUnit{Name: "main", ExecType: "exec_unit"},
+			unit:     &core.ExecutionUnit{Name: "main"},
 			resource: &core.Persist{Name: "redisNode", Kind: core.PersistRedisNodeKind},
 			values: []core.EnvironmentVariable{
 				{Name: "REDISNODE_PERSIST_REDIS_HOST", Kind: "persist_redis_node", ResourceID: "redisNode", Value: string(core.HOST)},
@@ -309,7 +309,7 @@ func Test_generateEnvVarsForPersist(t *testing.T) {
 		},
 		{
 			name:     "redis cluster dependency",
-			unit:     &core.ExecutionUnit{Name: "main", ExecType: "exec_unit"},
+			unit:     &core.ExecutionUnit{Name: "main"},
 			resource: &core.Persist{Name: "redisCluster", Kind: core.PersistRedisClusterKind},
 			values: []core.EnvironmentVariable{
 				{Name: "REDISCLUSTER_PERSIST_REDIS_HOST", Kind: "persist_redis_cluster", ResourceID: "redisCluster", Value: string(core.HOST)},
diff --git a/pkg/provider/aws/infra_template_test.go b/pkg/provider/aws/infra_template_test.go
index 6f09e7d0..e273651e 100644
--- a/pkg/provider/aws/infra_template_test.go
+++ b/pkg/provider/aws/infra_template_test.go
@@ -23,12 +23,10 @@ func TestInfraTemplateModification(t *testing.T) {
 			name: "simple test",
 			results: []core.CloudResource{&core.Gateway{
 				Name:   "gw",
-				GWType: core.GatewayKind,
 				Routes: []core.Route{{Path: "/"}},
 			},
 				&core.ExecutionUnit{
-					Name:     "unit",
-					ExecType: eks,
+					Name: "unit",
 				},
 			},
 			cfg: config.Application{
@@ -57,8 +55,7 @@ func TestInfraTemplateModification(t *testing.T) {
 			name: "helm chart test",
 			results: []core.CloudResource{
 				&core.ExecutionUnit{
-					Name:     "unit",
-					ExecType: eks,
+					Name: "unit",
 				},
 				&kubernetes.KlothoHelmChart{Values: []kubernetes.Value{{
 					Type: string(kubernetes.ImageTransformation),
@@ -145,7 +142,6 @@ func Test_GenerateCloudfrontDistributions(t *testing.T) {
 			results: []core.CloudResource{
 				&core.Gateway{
 					Name:   "gw",
-					GWType: core.GatewayKind,
 					Routes: []core.Route{{Path: "/"}},
 				},
 			},
@@ -251,7 +247,6 @@ func Test_GenerateCloudfrontDistributions(t *testing.T) {
 				},
 				&core.Gateway{
 					Name:   "gw",
-					GWType: core.GatewayKind,
 					Routes: []core.Route{{Path: "/"}},
 				},
 			},