Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion cmd/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ var (
newKeyTranslations string

useAutomations bool

bulkDeleteKeyIds []int
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
bulkUpdateKeys string
bulkCreateKeys string
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
)

// keyCmd represents the key command
Expand Down Expand Up @@ -128,8 +132,71 @@ var keyDeleteCmd = &cobra.Command{
},
}

var keyBulkDeleteCmd = &cobra.Command{
Use: "bulk-delete",
Short: "Delete multiple keys",
Long: "Deletes multiple keys from the project. Requires Manage keys admin right.",
RunE: func(*cobra.Command, []string) error {
var keyIDs []int64
for _, id := range bulkDeleteKeyIds {
keyIDs = append(keyIDs, int64(id))
}

resp, err := Api.Keys().BulkDelete(projectId, keyIDs)
if err != nil {
return err
}
return printJson(resp)
},
}

var keyBulkUpdateCmd = &cobra.Command{
Use: "bulk-update",
Short: "Update multiple keys",
Long: "Updates multiple keys in the project. Requires Manage keys admin right.",
RunE: func(*cobra.Command, []string) error {
var keys []lokalise.BulkUpdateKey
if err := json.Unmarshal([]byte(bulkUpdateKeys), &keys); err != nil {
return err
}

resp, err := Api.Keys().BulkUpdate(
projectId,
keys,
lokalise.WithAutomations(useAutomations),
)
if err != nil {
return err
}
return printJson(resp)
},
}

var keyBulkCreateCmd = &cobra.Command{
Use: "bulk-create",
Short: "Create multiple keys",
Long: "Creates multiple keys in the project from a JSON array.",
RunE: func(*cobra.Command, []string) error {
var keys []lokalise.NewKey
if err := json.Unmarshal([]byte(bulkCreateKeys), &keys); err != nil {
return err
}

resp, err := Api.Keys().Create(
projectId,
keys,
lokalise.WithAutomations(useAutomations),
)
if err != nil {
return err
}
return printJson(resp)
},
}

func init() {
keyCmd.AddCommand(keyListCmd, keyCreateCmd, keyRetrieveCmd, keyUpdateCmd, keyDeleteCmd)
keyCmd.AddCommand(keyListCmd, keyCreateCmd, keyRetrieveCmd, keyUpdateCmd, keyDeleteCmd,
keyBulkDeleteCmd, keyBulkUpdateCmd, keyBulkCreateCmd)
rootCmd.AddCommand(keyCmd)

// common for all Comment cmd`s
Expand Down Expand Up @@ -194,6 +261,27 @@ func init() {
keyRetrieveCmd.Flags().Uint8Var(&keyListOpts.DisableReferences, "disable-references", 0, "Whether to disable key references.")

flagKeyId(keyDeleteCmd)

// Bulk delete
keyBulkDeleteCmd.Flags().IntSliceVar(&bulkDeleteKeyIds, "key-ids", []int{},
"Comma-separated list of key IDs to delete (required).")
_ = keyBulkDeleteCmd.MarkFlagRequired("key-ids")

// Bulk update
fs = keyBulkUpdateCmd.Flags()
fs.StringVar(&bulkUpdateKeys, "keys", "",
"JSON array of key objects to update. Each object must contain key_id and fields to update (required).")
_ = keyBulkUpdateCmd.MarkFlagRequired("keys")
fs.BoolVar(&useAutomations, "use-automations", true,
"Whether to run automations on the updated key translations.")

// Bulk create
fs = keyBulkCreateCmd.Flags()
fs.StringVar(&bulkCreateKeys, "keys", "",
"JSON array of key objects to create. Each object should contain key_name, platforms, and other fields (required).")
_ = keyBulkCreateCmd.MarkFlagRequired("keys")
fs.BoolVar(&useAutomations, "use-automations", true,
"Whether to run automations on the new key translations.")
}

func flagKeyId(cmd *cobra.Command) {
Expand Down
185 changes: 185 additions & 0 deletions cmd/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package cmd

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"testing"

"github.com/spf13/cobra"
)

func TestKeyBulkDelete(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc(
fmt.Sprintf("/api2/projects/%s/keys", testProjectID),
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
testMethod(t, r, "DELETE")
testHeader(t, r, "X-Api-Token", testApiToken)

data := `{"keys":[123,456]}`
req := new(bytes.Buffer)
_ = json.Compact(req, []byte(data))
testBody(t, r, req.String())

_, _ = fmt.Fprint(w, `{
"project_id": "`+testProjectID+`",
"keys_removed": true,
"keys_locked": 0
}`)
})

args := []string{"key", "bulk-delete", "--key-ids=123,456", "--project-id=" + testProjectID}
rootCmd.SetArgs(args)
keyBulkDeleteCmd.PreRun = func(cmd *cobra.Command, args []string) {
Api = client
}

if err := rootCmd.Execute(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

func TestKeyBulkUpdate(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc(
fmt.Sprintf("/api2/projects/%s/keys", testProjectID),
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
testMethod(t, r, "PUT")
testHeader(t, r, "X-Api-Token", testApiToken)

_, _ = fmt.Fprint(w, `{
"project_id": "`+testProjectID+`",
"keys": [
{
"key_id": 123,
"key_name": {
"ios": "updated_key",
"android": "updated_key",
"web": "updated_key",
"other": "updated_key"
},
"platforms": ["web"],
"filenames": {},
"description": "Updated description",
"tags": ["updated"],
"comments": [],
"screenshots": [],
"translations": []
}
]
}`)
})

keysJSON := `[{"key_id":123,"description":"Updated description","tags":["updated"]}]`
args := []string{"key", "bulk-update", "--keys=" + keysJSON, "--project-id=" + testProjectID}
rootCmd.SetArgs(args)
keyBulkUpdateCmd.PreRun = func(cmd *cobra.Command, args []string) {
Api = client
}

if err := rootCmd.Execute(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

func TestKeyBulkCreate(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc(
fmt.Sprintf("/api2/projects/%s/keys", testProjectID),
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
testMethod(t, r, "POST")
testHeader(t, r, "X-Api-Token", testApiToken)

_, _ = fmt.Fprint(w, `{
"project_id": "`+testProjectID+`",
"keys": [
{
"key_id": 789,
"key_name": {
"ios": "welcome",
"android": "welcome",
"web": "welcome",
"other": "welcome"
},
"platforms": ["web"],
"filenames": {},
"description": "",
"tags": [],
"comments": [],
"screenshots": [],
"translations": []
},
{
"key_id": 790,
"key_name": {
"ios": "goodbye",
"android": "goodbye",
"web": "goodbye",
"other": "goodbye"
},
"platforms": ["ios", "android"],
"filenames": {},
"description": "",
"tags": ["v2"],
"comments": [],
"screenshots": [],
"translations": []
}
]
}`)
})

keysJSON := `[{"key_name":"welcome","platforms":["web"],"tags":[]},{"key_name":"goodbye","platforms":["ios","android"],"tags":["v2"]}]`
args := []string{"key", "bulk-create", "--keys=" + keysJSON, "--project-id=" + testProjectID}
rootCmd.SetArgs(args)
keyBulkCreateCmd.PreRun = func(cmd *cobra.Command, args []string) {
Api = client
}

if err := rootCmd.Execute(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

func TestKeyBulkUpdate_InvalidJSON(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()

args := []string{"key", "bulk-update", "--keys=not-json", "--project-id=" + testProjectID}
rootCmd.SetArgs(args)
keyBulkUpdateCmd.PreRun = func(cmd *cobra.Command, args []string) {
Api = client
}

err := rootCmd.Execute()
if err == nil {
t.Error("Expected error for invalid JSON, got nil")
}
}

func TestKeyBulkCreate_InvalidJSON(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()

args := []string{"key", "bulk-create", "--keys=not-json", "--project-id=" + testProjectID}
rootCmd.SetArgs(args)
keyBulkCreateCmd.PreRun = func(cmd *cobra.Command, args []string) {
Api = client
}

err := rootCmd.Execute()
if err == nil {
t.Error("Expected error for invalid JSON, got nil")
}
}