diff --git a/api/overlay.yaml b/api/overlay.yaml index 92b28afae9..b455dcc796 100644 --- a/api/overlay.yaml +++ b/api/overlay.yaml @@ -26,3 +26,6 @@ actions: - target: $.components.schemas.*.properties.connectionString description: Removes deprecated field that conflicts with naming convention remove: true +- target: $.components.schemas.*.properties.private_jwk.discriminator + description: Replaces discriminated union with concrete type + remove: true diff --git a/cmd/functions.go b/cmd/functions.go index cc5ffa53de..6b5684bc21 100644 --- a/cmd/functions.go +++ b/cmd/functions.go @@ -58,6 +58,7 @@ var ( useLegacyBundle bool noVerifyJWT = new(bool) importMapPath string + prune bool functionsDeployCmd = &cobra.Command{ Use: "deploy [Function name]", @@ -73,7 +74,7 @@ var ( } else if maxJobs > 1 { return errors.New("--jobs must be used together with --use-api") } - return deploy.Run(cmd.Context(), args, useDocker, noVerifyJWT, importMapPath, maxJobs, afero.NewOsFs()) + return deploy.Run(cmd.Context(), args, useDocker, noVerifyJWT, importMapPath, maxJobs, prune, afero.NewOsFs()) }, } @@ -139,6 +140,7 @@ func init() { cobra.CheckErr(deployFlags.MarkHidden("legacy-bundle")) deployFlags.UintVarP(&maxJobs, "jobs", "j", 1, "Maximum number of parallel jobs.") deployFlags.BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.") + deployFlags.BoolVar(&prune, "prune", false, "Delete Functions that exist in Supabase project but not locally.") deployFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") deployFlags.StringVar(&importMapPath, "import-map", "", "Path to import map file.") functionsServeCmd.Flags().BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.") diff --git a/cmd/root.go b/cmd/root.go index a2cc6f0a7f..d053045e89 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -232,6 +232,7 @@ func init() { }) flags := rootCmd.PersistentFlags() + flags.Bool("yes", false, "answer yes to all prompts") flags.Bool("debug", false, "output debug logs to stderr") flags.String("workdir", "", "path to a Supabase project directory") flags.Bool("experimental", false, "enable experimental features") diff --git a/internal/functions/delete/delete.go b/internal/functions/delete/delete.go index 47d5957efa..5e087e71e1 100644 --- a/internal/functions/delete/delete.go +++ b/internal/functions/delete/delete.go @@ -11,27 +11,29 @@ import ( ) func Run(ctx context.Context, slug string, projectRef string, fsys afero.Fs) error { - // 1. Sanity checks. - { - if err := utils.ValidateFunctionSlug(slug); err != nil { - return err - } + if err := utils.ValidateFunctionSlug(slug); err != nil { + return err } + if err := Undeploy(ctx, projectRef, slug); err != nil { + return err + } + fmt.Printf("Deleted Function %s from project %s.\n", utils.Aqua(slug), utils.Aqua(projectRef)) + return nil +} - // 2. Delete Function. +var ErrNoDelete = errors.New("nothing to delete") + +func Undeploy(ctx context.Context, projectRef string, slug string) error { resp, err := utils.GetSupabase().V1DeleteAFunctionWithResponse(ctx, projectRef, slug) if err != nil { return errors.Errorf("failed to delete function: %w", err) } switch resp.StatusCode() { case http.StatusNotFound: - return errors.New("Function " + utils.Aqua(slug) + " does not exist on the Supabase project.") + return errors.Errorf("Function %s does not exist on the Supabase project: %w", slug, ErrNoDelete) case http.StatusOK: - break + return nil default: - return errors.New("Failed to delete Function " + utils.Aqua(slug) + " on the Supabase project: " + string(resp.Body)) + return errors.Errorf("unexpected delete function status %d: %s", resp.StatusCode(), string(resp.Body)) } - - fmt.Println("Deleted Function " + utils.Aqua(slug) + " from project " + utils.Aqua(projectRef) + ".") - return nil } diff --git a/internal/functions/delete/delete_test.go b/internal/functions/delete/delete_test.go index 110208ea90..79bd2bb5ac 100644 --- a/internal/functions/delete/delete_test.go +++ b/internal/functions/delete/delete_test.go @@ -73,7 +73,7 @@ func TestDeleteCommand(t *testing.T) { // Run test err := Run(context.Background(), slug, project, fsys) // Check error - assert.ErrorContains(t, err, "Function test-func does not exist on the Supabase project.") + assert.ErrorIs(t, err, ErrNoDelete) assert.Empty(t, apitest.ListUnmatchedRequests()) }) @@ -88,7 +88,7 @@ func TestDeleteCommand(t *testing.T) { // Run test err := Run(context.Background(), slug, project, fsys) // Check error - assert.ErrorContains(t, err, "Failed to delete Function test-func on the Supabase project:") + assert.ErrorContains(t, err, "unexpected delete function status 503:") assert.Empty(t, apitest.ListUnmatchedRequests()) }) } diff --git a/internal/functions/deploy/deploy.go b/internal/functions/deploy/deploy.go index 470a543040..8c815e7795 100644 --- a/internal/functions/deploy/deploy.go +++ b/internal/functions/deploy/deploy.go @@ -9,13 +9,15 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" + "github.com/supabase/cli/internal/functions/delete" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/flags" + "github.com/supabase/cli/pkg/api" "github.com/supabase/cli/pkg/config" "github.com/supabase/cli/pkg/function" ) -func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool, importMapPath string, maxJobs uint, fsys afero.Fs) error { +func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool, importMapPath string, maxJobs uint, prune bool, fsys afero.Fs) error { // Load function config and project id if err := flags.LoadConfig(fsys); err != nil { return err @@ -49,6 +51,7 @@ func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool, if err != nil { return err } + // Deploy new and updated functions opt := function.WithMaxJobs(maxJobs) if useDocker { if utils.IsDockerRunning(ctx) { @@ -67,7 +70,10 @@ func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool, fmt.Printf("Deployed Functions on project %s: %s\n", utils.Aqua(flags.ProjectRef), strings.Join(slugs, ", ")) url := fmt.Sprintf("%s/project/%v/functions", utils.GetSupabaseDashboardURL(), flags.ProjectRef) fmt.Println("You can inspect your deployment in the Dashboard: " + url) - return nil + if !prune { + return nil + } + return pruneFunctions(ctx, functionConfig) } func GetFunctionSlugs(fsys afero.Fs) (slugs []string, err error) { @@ -155,3 +161,51 @@ func GetFunctionConfig(slugs []string, importMapPath string, noVerifyJWT *bool, } return functionConfig, nil } + +// pruneFunctions deletes functions that exist remotely but not locally +func pruneFunctions(ctx context.Context, functionConfig config.FunctionConfig) error { + resp, err := utils.GetSupabase().V1ListAllFunctionsWithResponse(ctx, flags.ProjectRef) + if err != nil { + return errors.Errorf("failed to list functions: %w", err) + } else if resp.JSON200 == nil { + return errors.Errorf("unexpected list functions status %d: %s", resp.StatusCode(), string(resp.Body)) + } + // No need to delete disabled functions + var toDelete []string + for _, deployed := range *resp.JSON200 { + if deployed.Status == api.FunctionResponseStatusREMOVED { + continue + } else if _, exists := functionConfig[deployed.Slug]; exists { + continue + } + toDelete = append(toDelete, deployed.Slug) + } + if len(toDelete) == 0 { + fmt.Fprintln(os.Stderr, "No functions to prune.") + return nil + } + // Confirm before pruning functions + msg := fmt.Sprintln(confirmPruneAll(toDelete)) + if shouldDelete, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil { + return err + } else if !shouldDelete { + return errors.New(context.Canceled) + } + for _, slug := range toDelete { + fmt.Fprintln(os.Stderr, "Deleting Function:", slug) + if err := delete.Undeploy(ctx, flags.ProjectRef, slug); errors.Is(err, delete.ErrNoDelete) { + fmt.Fprintln(utils.GetDebugLogger(), err) + } else if err != nil { + return err + } + } + return nil +} + +func confirmPruneAll(pending []string) string { + msg := fmt.Sprintln("Do you want to delete the following functions?") + for _, slug := range pending { + msg += fmt.Sprintf(" • %s\n", utils.Bold(slug)) + } + return msg +} diff --git a/internal/functions/deploy/deploy_test.go b/internal/functions/deploy/deploy_test.go index 9f0fb79dbf..e746da9279 100644 --- a/internal/functions/deploy/deploy_test.go +++ b/internal/functions/deploy/deploy_test.go @@ -11,6 +11,7 @@ import ( "github.com/h2non/gock" "github.com/spf13/afero" + "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/supabase/cli/internal/testing/apitest" @@ -74,7 +75,7 @@ func TestDeployCommand(t *testing.T) { } // Run test noVerifyJWT := true - err = Run(context.Background(), functions, true, &noVerifyJWT, "", 1, fsys) + err = Run(context.Background(), functions, true, &noVerifyJWT, "", 1, false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -129,7 +130,7 @@ import_map = "./import_map.json" outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug)) require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644)) // Run test - err = Run(context.Background(), nil, true, nil, "", 1, fsys) + err = Run(context.Background(), nil, true, nil, "", 1, false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -182,7 +183,7 @@ import_map = "./import_map.json" outputDir := filepath.Join(utils.TempDir, ".output_enabled-func") require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644)) // Run test - err = Run(context.Background(), nil, true, nil, "", 1, fsys) + err = Run(context.Background(), nil, true, nil, "", 1, false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -193,7 +194,7 @@ import_map = "./import_map.json" fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) // Run test - err := Run(context.Background(), []string{"_invalid"}, true, nil, "", 1, fsys) + err := Run(context.Background(), []string{"_invalid"}, true, nil, "", 1, false, fsys) // Check error assert.ErrorContains(t, err, "Invalid Function name.") }) @@ -203,7 +204,7 @@ import_map = "./import_map.json" fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) // Run test - err := Run(context.Background(), nil, true, nil, "", 1, fsys) + err := Run(context.Background(), nil, true, nil, "", 1, false, fsys) // Check error assert.ErrorContains(t, err, "No Functions specified or found in supabase/functions") }) @@ -249,7 +250,7 @@ verify_jwt = false outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug)) require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644)) // Run test - assert.NoError(t, Run(context.Background(), []string{slug}, true, nil, "", 1, fsys)) + assert.NoError(t, Run(context.Background(), []string{slug}, true, nil, "", 1, false, fsys)) // Validate api assert.Empty(t, apitest.ListUnmatchedRequests()) }) @@ -295,8 +296,8 @@ verify_jwt = false outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug)) require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644)) // Run test - noVerifyJwt := false - assert.NoError(t, Run(context.Background(), []string{slug}, true, &noVerifyJwt, "", 1, fsys)) + noVerifyJWT := false + assert.NoError(t, Run(context.Background(), []string{slug}, true, &noVerifyJWT, "", 1, false, fsys)) // Validate api assert.Empty(t, apitest.ListUnmatchedRequests()) }) @@ -372,3 +373,90 @@ func TestImportMapPath(t *testing.T) { assert.Equal(t, path, fc["test"].ImportMap) }) } + +func TestPruneFunctions(t *testing.T) { + flags.ProjectRef = apitest.RandomProjectRef() + token := apitest.RandomAccessToken(t) + t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) + viper.Set("YES", true) + + t.Run("prunes functions not in local directory", func(t *testing.T) { + // Setup function entrypoints + localFunctions := config.FunctionConfig{ + "local-func-1": {Enabled: true}, + "local-func-2": {Enabled: true}, + } + // Setup mock api - remote functions include local ones plus orphaned ones + defer gock.OffAll() + remoteFunctions := []api.FunctionResponse{ + {Slug: "local-func-1"}, + {Slug: "local-func-2"}, + {Slug: "orphaned-func-1"}, + {Slug: "orphaned-func-2"}, + } + gock.New(utils.DefaultApiHost). + Get("/v1/projects/" + flags.ProjectRef + "/functions"). + Reply(http.StatusOK). + JSON(remoteFunctions) + gock.New(utils.DefaultApiHost). + Delete("/v1/projects/" + flags.ProjectRef + "/functions/orphaned-func-1"). + Reply(http.StatusOK) + gock.New(utils.DefaultApiHost). + Delete("/v1/projects/" + flags.ProjectRef + "/functions/orphaned-func-2"). + Reply(http.StatusOK) + // Run test with prune and force (to skip confirmation) + err := pruneFunctions(context.Background(), localFunctions) + // Check error + assert.NoError(t, err) + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("skips pruning when no orphaned functions", func(t *testing.T) { + // Setup function entrypoints + localFunctions := config.FunctionConfig{ + "local-func-1": {}, + "local-func-2": {}, + } + // Setup mock api - remote functions match local ones exactly + defer gock.OffAll() + remoteFunctions := []api.FunctionResponse{ + {Slug: "local-func-1"}, + {Slug: "local-func-2"}, + {Slug: "orphaned-func-1", Status: api.FunctionResponseStatusREMOVED}, + {Slug: "orphaned-func-2", Status: api.FunctionResponseStatusREMOVED}, + } + gock.New(utils.DefaultApiHost). + Get("/v1/projects/" + flags.ProjectRef + "/functions"). + Reply(http.StatusOK). + JSON(remoteFunctions) + // Run test with prune and force + err := pruneFunctions(context.Background(), localFunctions) + // Check error + assert.NoError(t, err) + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("handles 404 on delete gracefully", func(t *testing.T) { + // Setup function entrypoints + localFunctions := config.FunctionConfig{"local-func": {}} + // Setup mock api + defer gock.OffAll() + remoteFunctions := []api.FunctionResponse{ + {Slug: "local-func"}, + {Slug: "orphaned-func"}, + } + gock.New(utils.DefaultApiHost). + Get("/v1/projects/" + flags.ProjectRef + "/functions"). + Reply(http.StatusOK). + JSON(remoteFunctions) + // Mock delete endpoint with 404 (function already deleted) + gock.New(utils.DefaultApiHost). + Delete("/v1/projects/" + flags.ProjectRef + "/functions/orphaned-func"). + Reply(http.StatusNotFound) + // Run test with prune and force + err := pruneFunctions(context.Background(), localFunctions) + // Check error + assert.NoError(t, err) + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) +} diff --git a/internal/utils/console.go b/internal/utils/console.go index 85bebdc1ee..f80099aa34 100644 --- a/internal/utils/console.go +++ b/internal/utils/console.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-errors/errors" + "github.com/spf13/viper" "github.com/supabase/cli/pkg/cast" "golang.org/x/term" ) @@ -66,6 +67,10 @@ func (c *Console) PromptYesNo(ctx context.Context, label string, def bool) (bool choices = "y/N" } labelWithChoice := fmt.Sprintf("%s [%s] ", label, choices) + if viper.GetBool("YES") { + fmt.Fprintln(os.Stderr, labelWithChoice+"y") + return true, nil + } // Any error will be handled as default value input, err := c.PromptText(ctx, labelWithChoice) if len(input) > 0 { diff --git a/pkg/api/types.gen.go b/pkg/api/types.gen.go index 726ccf532d..1509530e37 100644 --- a/pkg/api/types.gen.go +++ b/pkg/api/types.gen.go @@ -211,6 +211,41 @@ const ( CreateSigningKeyBodyAlgorithmRS256 CreateSigningKeyBodyAlgorithm = "RS256" ) +// Defines values for CreateSigningKeyBodyPrivateJwk0E. +const ( + AQAB CreateSigningKeyBodyPrivateJwk0E = "AQAB" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk0Kty. +const ( + RSA CreateSigningKeyBodyPrivateJwk0Kty = "RSA" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk1Crv. +const ( + P256 CreateSigningKeyBodyPrivateJwk1Crv = "P-256" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk1Kty. +const ( + EC CreateSigningKeyBodyPrivateJwk1Kty = "EC" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk2Crv. +const ( + Ed25519 CreateSigningKeyBodyPrivateJwk2Crv = "Ed25519" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk2Kty. +const ( + OKP CreateSigningKeyBodyPrivateJwk2Kty = "OKP" +) + +// Defines values for CreateSigningKeyBodyPrivateJwk3Kty. +const ( + Oct CreateSigningKeyBodyPrivateJwk3Kty = "oct" +) + // Defines values for CreateSigningKeyBodyStatus. const ( CreateSigningKeyBodyStatusInUse CreateSigningKeyBodyStatus = "in_use" @@ -1444,13 +1479,76 @@ type CreateSecretBody = []struct { // CreateSigningKeyBody defines model for CreateSigningKeyBody. type CreateSigningKeyBody struct { - Algorithm CreateSigningKeyBodyAlgorithm `json:"algorithm"` - Status *CreateSigningKeyBodyStatus `json:"status,omitempty"` + Algorithm CreateSigningKeyBodyAlgorithm `json:"algorithm"` + PrivateJwk *CreateSigningKeyBody_PrivateJwk `json:"private_jwk,omitempty"` + Status *CreateSigningKeyBodyStatus `json:"status,omitempty"` } // CreateSigningKeyBodyAlgorithm defines model for CreateSigningKeyBody.Algorithm. type CreateSigningKeyBodyAlgorithm string +// CreateSigningKeyBodyPrivateJwk0 defines model for . +type CreateSigningKeyBodyPrivateJwk0 struct { + D string `json:"d"` + Dp string `json:"dp"` + Dq string `json:"dq"` + E CreateSigningKeyBodyPrivateJwk0E `json:"e"` + Kty CreateSigningKeyBodyPrivateJwk0Kty `json:"kty"` + N string `json:"n"` + P string `json:"p"` + Q string `json:"q"` + Qi string `json:"qi"` +} + +// CreateSigningKeyBodyPrivateJwk0E defines model for CreateSigningKeyBody.PrivateJwk.0.E. +type CreateSigningKeyBodyPrivateJwk0E string + +// CreateSigningKeyBodyPrivateJwk0Kty defines model for CreateSigningKeyBody.PrivateJwk.0.Kty. +type CreateSigningKeyBodyPrivateJwk0Kty string + +// CreateSigningKeyBodyPrivateJwk1 defines model for . +type CreateSigningKeyBodyPrivateJwk1 struct { + Crv CreateSigningKeyBodyPrivateJwk1Crv `json:"crv"` + D string `json:"d"` + Kty CreateSigningKeyBodyPrivateJwk1Kty `json:"kty"` + X string `json:"x"` + Y string `json:"y"` +} + +// CreateSigningKeyBodyPrivateJwk1Crv defines model for CreateSigningKeyBody.PrivateJwk.1.Crv. +type CreateSigningKeyBodyPrivateJwk1Crv string + +// CreateSigningKeyBodyPrivateJwk1Kty defines model for CreateSigningKeyBody.PrivateJwk.1.Kty. +type CreateSigningKeyBodyPrivateJwk1Kty string + +// CreateSigningKeyBodyPrivateJwk2 defines model for . +type CreateSigningKeyBodyPrivateJwk2 struct { + Crv CreateSigningKeyBodyPrivateJwk2Crv `json:"crv"` + D string `json:"d"` + Kty CreateSigningKeyBodyPrivateJwk2Kty `json:"kty"` + X string `json:"x"` +} + +// CreateSigningKeyBodyPrivateJwk2Crv defines model for CreateSigningKeyBody.PrivateJwk.2.Crv. +type CreateSigningKeyBodyPrivateJwk2Crv string + +// CreateSigningKeyBodyPrivateJwk2Kty defines model for CreateSigningKeyBody.PrivateJwk.2.Kty. +type CreateSigningKeyBodyPrivateJwk2Kty string + +// CreateSigningKeyBodyPrivateJwk3 defines model for . +type CreateSigningKeyBodyPrivateJwk3 struct { + K string `json:"k"` + Kty CreateSigningKeyBodyPrivateJwk3Kty `json:"kty"` +} + +// CreateSigningKeyBodyPrivateJwk3Kty defines model for CreateSigningKeyBody.PrivateJwk.3.Kty. +type CreateSigningKeyBodyPrivateJwk3Kty string + +// CreateSigningKeyBody_PrivateJwk defines model for CreateSigningKeyBody.PrivateJwk. +type CreateSigningKeyBody_PrivateJwk struct { + union json.RawMessage +} + // CreateSigningKeyBodyStatus defines model for CreateSigningKeyBody.Status. type CreateSigningKeyBodyStatus string @@ -1962,7 +2060,8 @@ type ProjectUpgradeEligibilityResponse struct { PostgresVersion ProjectUpgradeEligibilityResponseTargetUpgradeVersionsPostgresVersion `json:"postgres_version"` ReleaseChannel ProjectUpgradeEligibilityResponseTargetUpgradeVersionsReleaseChannel `json:"release_channel"` } `json:"target_upgrade_versions"` - UnsupportedExtensions []string `json:"unsupported_extensions"` + UnsupportedExtensions []string `json:"unsupported_extensions"` + UserDefinedObjectsInInternalSchemas []string `json:"user_defined_objects_in_internal_schemas"` } // ProjectUpgradeEligibilityResponseCurrentAppVersionReleaseChannel defines model for ProjectUpgradeEligibilityResponse.CurrentAppVersionReleaseChannel. @@ -3552,6 +3651,120 @@ func (t *ApplyProjectAddonBody_AddonVariant) UnmarshalJSON(b []byte) error { return err } +// AsCreateSigningKeyBodyPrivateJwk0 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk0 +func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk0() (CreateSigningKeyBodyPrivateJwk0, error) { + var body CreateSigningKeyBodyPrivateJwk0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateSigningKeyBodyPrivateJwk0 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk0 +func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk0(v CreateSigningKeyBodyPrivateJwk0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateSigningKeyBodyPrivateJwk0 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk0 +func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk0(v CreateSigningKeyBodyPrivateJwk0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsCreateSigningKeyBodyPrivateJwk1 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk1 +func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk1() (CreateSigningKeyBodyPrivateJwk1, error) { + var body CreateSigningKeyBodyPrivateJwk1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateSigningKeyBodyPrivateJwk1 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk1 +func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk1(v CreateSigningKeyBodyPrivateJwk1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateSigningKeyBodyPrivateJwk1 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk1 +func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk1(v CreateSigningKeyBodyPrivateJwk1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsCreateSigningKeyBodyPrivateJwk2 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk2 +func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk2() (CreateSigningKeyBodyPrivateJwk2, error) { + var body CreateSigningKeyBodyPrivateJwk2 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateSigningKeyBodyPrivateJwk2 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk2 +func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk2(v CreateSigningKeyBodyPrivateJwk2) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateSigningKeyBodyPrivateJwk2 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk2 +func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk2(v CreateSigningKeyBodyPrivateJwk2) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsCreateSigningKeyBodyPrivateJwk3 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk3 +func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk3() (CreateSigningKeyBodyPrivateJwk3, error) { + var body CreateSigningKeyBodyPrivateJwk3 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateSigningKeyBodyPrivateJwk3 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk3 +func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk3(v CreateSigningKeyBodyPrivateJwk3) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateSigningKeyBodyPrivateJwk3 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk3 +func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk3(v CreateSigningKeyBodyPrivateJwk3) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t CreateSigningKeyBody_PrivateJwk) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *CreateSigningKeyBody_PrivateJwk) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + // AsListProjectAddonsResponseAvailableAddonsVariantsId0 returns the union data inside the ListProjectAddonsResponse_AvailableAddons_Variants_Id as a ListProjectAddonsResponseAvailableAddonsVariantsId0 func (t ListProjectAddonsResponse_AvailableAddons_Variants_Id) AsListProjectAddonsResponseAvailableAddonsVariantsId0() (ListProjectAddonsResponseAvailableAddonsVariantsId0, error) { var body ListProjectAddonsResponseAvailableAddonsVariantsId0