diff --git a/command/login.go b/command/login.go index 194bbb9..ed34438 100644 --- a/command/login.go +++ b/command/login.go @@ -161,6 +161,7 @@ logged in system. non-production server to login to (values are 'pre', enumflag.New(&selectedRelease, "release", ScratchReleaseIds, enumflag.EnumCaseInsensitive), "release", "Salesforce release for scratch org: preview (next release) or previous") + scratchCmd.Flags().Int("duration", 7, "number of days before the scratch org expires (1-30)") loginCmd.AddCommand(scratchCmd) RootCmd.AddCommand(loginCmd) @@ -219,7 +220,8 @@ Examples: force login scratch --product communities force login scratch --product healthcloud force login scratch --release preview - force login scratch --release previous`, + force login scratch --release previous + force login scratch --duration 14`, Run: func(cmd *cobra.Command, args []string) { scratchUser, _ := cmd.Flags().GetString("username") scratchNamespace, _ := cmd.Flags().GetString("namespace") @@ -228,7 +230,8 @@ Examples: edition := ScratchEditionIds[selectedEdition][0] allSettings := expandProductsToSettings(selectedProducts, selectedSettings) release := ScratchReleaseIds[selectedRelease][0] - scratchLogin(scratchUser, allFeatures, edition, allSettings, scratchNamespace, release) + duration, _ := cmd.Flags().GetInt("duration") + scratchLogin(scratchUser, allFeatures, edition, allSettings, scratchNamespace, release, duration) }, } @@ -355,8 +358,8 @@ func expandProductsToSettings(products []ScratchProduct, settings []ScratchSetti return uniqueSettings } -func scratchLogin(scratchUser string, features []string, edition string, settings []string, namespace string, release string) { - _, err := ForceScratchCreateLoginAndSaveWithRelease(scratchUser, features, edition, settings, namespace, release, os.Stderr) +func scratchLogin(scratchUser string, features []string, edition string, settings []string, namespace string, release string, duration int) { + _, err := ForceScratchCreateLoginAndSaveWithDuration(scratchUser, features, edition, settings, namespace, release, duration, os.Stderr) if err != nil { ErrorAndExit(err.Error()) } diff --git a/lib/auth.go b/lib/auth.go index e1f97c7..0c28411 100644 --- a/lib/auth.go +++ b/lib/auth.go @@ -139,13 +139,17 @@ func ForceScratchCreateLoginAndSaveWithNamespace(scratchUser string, features [] } func ForceScratchCreateLoginAndSaveWithRelease(scratchUser string, features []string, edition string, settings []string, namespace string, release string, output *os.File) (username string, err error) { + return ForceScratchCreateLoginAndSaveWithDuration(scratchUser, features, edition, settings, namespace, release, 7, output) +} + +func ForceScratchCreateLoginAndSaveWithDuration(scratchUser string, features []string, edition string, settings []string, namespace string, release string, duration int, output *os.File) (username string, err error) { force, err := ActiveForce() if err != nil { err = errors.New("You must be logged into a Dev Hub org to authenticate as a scratch org user.") return } fmt.Fprintln(os.Stderr, "Creating new Scratch Org...") - scratchOrgId, err := force.CreateScratchOrgWithRelease(scratchUser, features, edition, settings, namespace, release) + scratchOrgId, err := force.CreateScratchOrgWithDuration(scratchUser, features, edition, settings, namespace, release, duration) if err != nil { return } diff --git a/lib/scratch.go b/lib/scratch.go index 1ee44a8..3acc1d0 100644 --- a/lib/scratch.go +++ b/lib/scratch.go @@ -281,10 +281,15 @@ func (f *Force) CreateScratchOrgWithUserFeaturesEditionSettingsAndNamespace(user } func (f *Force) CreateScratchOrgWithRelease(username string, features []string, edition string, settings []string, namespace string, release string) (id string, err error) { + return f.CreateScratchOrgWithDuration(username, features, edition, settings, namespace, release, 7) +} + +func (f *Force) CreateScratchOrgWithDuration(username string, features []string, edition string, settings []string, namespace string, release string, duration int) (id string, err error) { params := make(map[string]string) params["ConnectedAppCallbackUrl"] = "http://localhost:1717/OauthRedirect" params["ConnectedAppConsumerKey"] = "PlatformCLI" params["Country"] = "US" + params["DurationDays"] = fmt.Sprintf("%d", duration) if edition != "" { params["Edition"] = edition diff --git a/lib/scratch_test.go b/lib/scratch_test.go index f33abcd..0ea1954 100644 --- a/lib/scratch_test.go +++ b/lib/scratch_test.go @@ -244,3 +244,69 @@ func TestWaitForScratchOrgReady_times_out(t *testing.T) { t.Errorf("Expected timeout error, got: %v", err) } } + +func TestCreateScratchOrgWithDuration_sets_DurationDays(t *testing.T) { + var receivedBody map[string]interface{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" && strings.Contains(r.URL.Path, "ScratchOrgInfo") { + json.NewDecoder(r.Body).Decode(&receivedBody) + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(map[string]interface{}{ + "id": "2SRp0000000MFpOAM", + "success": true, + }) + return + } + w.WriteHeader(http.StatusNotFound) + })) + defer server.Close() + + force := &Force{ + Credentials: &ForceSession{ + InstanceUrl: server.URL, + AccessToken: "test-token", + }, + } + + _, err := force.CreateScratchOrgWithDuration("", []string{}, "", []string{}, "", "", 14) + + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + if receivedBody["DurationDays"] != "14" { + t.Errorf("Expected DurationDays to be '14', got: %v", receivedBody["DurationDays"]) + } +} + +func TestCreateScratchOrgWithRelease_uses_default_duration_of_7(t *testing.T) { + var receivedBody map[string]interface{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" && strings.Contains(r.URL.Path, "ScratchOrgInfo") { + json.NewDecoder(r.Body).Decode(&receivedBody) + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(map[string]interface{}{ + "id": "2SRp0000000MFpOAM", + "success": true, + }) + return + } + w.WriteHeader(http.StatusNotFound) + })) + defer server.Close() + + force := &Force{ + Credentials: &ForceSession{ + InstanceUrl: server.URL, + AccessToken: "test-token", + }, + } + + _, err := force.CreateScratchOrgWithRelease("", []string{}, "", []string{}, "", "") + + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + if receivedBody["DurationDays"] != "7" { + t.Errorf("Expected DurationDays to be '7', got: %v", receivedBody["DurationDays"]) + } +}