Skip to content

Commit 3dfa36b

Browse files
author
Paola Nicosia
committed
feat: add delete and put commands
1 parent 895853b commit 3dfa36b

File tree

11 files changed

+441
-28
lines changed

11 files changed

+441
-28
lines changed

internal/clioptions/clioptions.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ type CLIOptions struct {
7979
MarketplaceItemObjectID string
8080
MarketplaceFetchPublicItems bool
8181

82-
ItemTypeDefinitionName string
82+
ItemTypeDefinitionName string
83+
ItemTypeDefinitionFilePath string
8384

8485
FromCronJob string
8586

@@ -270,6 +271,15 @@ func (o *CLIOptions) AddItemTypeDefinitionNameFlag(flags *pflag.FlagSet) (flagNa
270271
return
271272
}
272273

274+
func (o *CLIOptions) AddItemTypeDefinitionFileFlag(cmd *cobra.Command) {
275+
cmd.Flags().StringVarP(&o.ItemTypeDefinitionFilePath, "file", "f", "", "paths to JSON/YAML file containing an item type definition")
276+
err := cmd.MarkFlagRequired("file")
277+
if err != nil {
278+
// the error is only due to a programming error (missing command), hence panic
279+
panic(err)
280+
}
281+
}
282+
273283
func (o *CLIOptions) AddCreateJobFlags(flags *pflag.FlagSet) {
274284
flags.StringVar(&o.FromCronJob, "from", "", "name of the cronjob to create a Job from")
275285
}

internal/cmd/item-type-definition.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func ItemTypeDefinitionCmd(options *clioptions.CLIOptions) *cobra.Command {
3737
cmd.AddCommand(itd.ListCmd(options))
3838
cmd.AddCommand(itd.GetCmd(options))
3939
cmd.AddCommand(itd.DeleteCmd(options))
40-
// cmd.AddCommand(itd.ApplyCmd(options))
40+
cmd.AddCommand(itd.PutCmd(options))
4141

4242
return cmd
4343
}

internal/cmd/item-type-definition/delete.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ import (
2323

2424
"github.com/mia-platform/miactl/internal/client"
2525
"github.com/mia-platform/miactl/internal/clioptions"
26-
"github.com/mia-platform/miactl/internal/resources/catalog"
27-
"github.com/mia-platform/miactl/internal/resources/marketplace"
26+
itd "github.com/mia-platform/miactl/internal/resources/item-type-definition"
2827
"github.com/mia-platform/miactl/internal/util"
2928
"github.com/spf13/cobra"
3029
)
@@ -41,13 +40,13 @@ const (
4140
4241
You need to specify the companyId and the item type definition name via the respective flags (recommended). The company-id flag can be omitted if it is already set in the context.
4342
`
44-
cmdUse = "delete { --name name --version version }"
43+
deleteCmdUse = "delete { --name name --version version }"
4544
)
4645

4746
func DeleteCmd(options *clioptions.CLIOptions) *cobra.Command {
4847
cmd := &cobra.Command{
49-
Use: cmdUse,
50-
Short: "Delete a Catalog item",
48+
Use: deleteCmdUse,
49+
Short: "Delete an Item Type Definition",
5150
Long: cmdDeleteLongDescription,
5251
SuggestFor: []string{"rm"},
5352
RunE: func(cmd *cobra.Command, _ []string) error {
@@ -56,14 +55,14 @@ func DeleteCmd(options *clioptions.CLIOptions) *cobra.Command {
5655
client, err := client.APIClientForConfig(restConfig)
5756
cobra.CheckErr(err)
5857

59-
canUseNewAPI, versionError := util.VersionCheck(cmd.Context(), client, 14, 0)
58+
canUseNewAPI, versionError := util.VersionCheck(cmd.Context(), client, 14, 1)
6059
if !canUseNewAPI || versionError != nil {
61-
return catalog.ErrUnsupportedCompanyVersion
60+
return itd.ErrUnsupportedCompanyVersion
6261
}
6362

6463
companyID := restConfig.CompanyID
6564
if len(companyID) == 0 {
66-
return marketplace.ErrMissingCompanyID
65+
return itd.ErrMissingCompanyID
6766
}
6867

6968
if options.MarketplaceItemVersion != "" && options.MarketplaceItemID != "" {
@@ -103,7 +102,7 @@ func deleteITD(ctx context.Context, client *client.APIClient, companyID, name st
103102
fmt.Println("item deleted successfully")
104103
return nil
105104
case http.StatusNotFound:
106-
return marketplace.ErrItemNotFound
105+
return itd.ErrItemNotFound
107106
default:
108107
if resp.StatusCode() >= http.StatusInternalServerError {
109108
return ErrServerDeleteItem

internal/cmd/item-type-definition/delete_test.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"github.com/mia-platform/miactl/internal/client"
2525
"github.com/mia-platform/miactl/internal/clioptions"
2626
itd "github.com/mia-platform/miactl/internal/resources/item-type-definition"
27-
"github.com/mia-platform/miactl/internal/resources/marketplace"
2827
"github.com/stretchr/testify/require"
2928
)
3029

@@ -39,7 +38,7 @@ func TestDeleteResourceCmd(t *testing.T) {
3938
require.NotNil(t, cmd)
4039
})
4140

42-
t.Run("should not run command when Console version is lower than 14.0.0", func(t *testing.T) {
41+
t.Run("should not run command when Console version is lower than 14.1.0", func(t *testing.T) {
4342
server := httptest.NewServer(unexecutedCmdMockServer(t))
4443
defer server.Close()
4544

@@ -48,14 +47,14 @@ func TestDeleteResourceCmd(t *testing.T) {
4847
opts.Endpoint = server.URL
4948

5049
cmd := DeleteCmd(opts)
51-
cmd.SetArgs([]string{"delete", "--item-id", "some-item-id", "--version", "1.0.0"})
50+
cmd.SetArgs([]string{"delete", "--name", "some-item-id"})
5251

5352
err := cmd.Execute()
5453
require.ErrorIs(t, err, itd.ErrUnsupportedCompanyVersion)
5554
})
5655
}
5756

58-
func deleteByItemIDAndVersionMockServer(t *testing.T,
57+
func deleteByItemNameMockServer(t *testing.T,
5958
statusCode int,
6059
mockName string,
6160
callsCount *int,
@@ -107,7 +106,7 @@ func TestDeleteItemByItemIDAndVersion(t *testing.T) {
107106

108107
statusCode: http.StatusNotFound,
109108

110-
expectedErr: marketplace.ErrItemNotFound,
109+
expectedErr: itd.ErrItemNotFound,
111110
expectedCalls: 1,
112111
},
113112
{
@@ -134,7 +133,7 @@ func TestDeleteItemByItemIDAndVersion(t *testing.T) {
134133
t.Run(tt.testName, func(t *testing.T) {
135134
callsCount := new(int)
136135
*callsCount = 0
137-
testServer := deleteByItemIDAndVersionMockServer(
136+
testServer := deleteByItemNameMockServer(
138137
t,
139138
tt.statusCode,
140139
tt.name,

internal/cmd/item-type-definition/get.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import (
2121

2222
"github.com/mia-platform/miactl/internal/client"
2323
"github.com/mia-platform/miactl/internal/clioptions"
24-
"github.com/mia-platform/miactl/internal/resources/catalog"
25-
"github.com/mia-platform/miactl/internal/resources/marketplace"
24+
itd "github.com/mia-platform/miactl/internal/resources/item-type-definition"
2625
"github.com/mia-platform/miactl/internal/util"
2726
"github.com/spf13/cobra"
2827
)
@@ -51,7 +50,7 @@ func GetCmd(options *clioptions.CLIOptions) *cobra.Command {
5150

5251
canUseNewAPI, versionError := util.VersionCheck(cmd.Context(), client, 14, 1)
5352
if !canUseNewAPI || versionError != nil {
54-
return catalog.ErrUnsupportedCompanyVersion
53+
return itd.ErrUnsupportedCompanyVersion
5554
}
5655

5756
serializedItem, err := getItemEncodedWithFormat(
@@ -77,7 +76,7 @@ func GetCmd(options *clioptions.CLIOptions) *cobra.Command {
7776

7877
func getItemEncodedWithFormat(ctx context.Context, client *client.APIClient, companyID, name, outputFormat string) (string, error) {
7978
if companyID == "" {
80-
return "", marketplace.ErrMissingCompanyID
79+
return "", itd.ErrMissingCompanyID
8180
}
8281
endpoint := fmt.Sprintf(getItdEndpoint, companyID, name)
8382
item, err := performGetITDRequest(ctx, client, endpoint)
@@ -94,7 +93,7 @@ func getItemEncodedWithFormat(ctx context.Context, client *client.APIClient, com
9493
return string(data), nil
9594
}
9695

97-
func performGetITDRequest(ctx context.Context, client *client.APIClient, endpoint string) (*marketplace.Item, error) {
96+
func performGetITDRequest(ctx context.Context, client *client.APIClient, endpoint string) (*itd.GenericItemTypeDefinition, error) {
9897
resp, err := client.Get().APIPath(endpoint).Do(ctx)
9998

10099
if err != nil {
@@ -105,14 +104,14 @@ func performGetITDRequest(ctx context.Context, client *client.APIClient, endpoin
105104
return nil, err
106105
}
107106

108-
var marketplaceItem *marketplace.Item
109-
if err := resp.ParseResponse(&marketplaceItem); err != nil {
107+
var itd *itd.GenericItemTypeDefinition
108+
if err := resp.ParseResponse(&itd); err != nil {
110109
return nil, fmt.Errorf("error parsing response body: %w", err)
111110
}
112111

113-
if marketplaceItem == nil {
112+
if itd == nil {
114113
return nil, fmt.Errorf("no item type definition returned in the response")
115114
}
116115

117-
return marketplaceItem, nil
116+
return itd, nil
118117
}

internal/cmd/item-type-definition/get_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const (
5757
}
5858
],
5959
"publisher": {
60-
"name": "Paola"
60+
"name": "publisher-name"
6161
}
6262
},
6363
"spec": {

internal/cmd/item-type-definition/list_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func unexecutedCmdMockServer(t *testing.T) http.HandlerFunc {
132132
t.Helper()
133133
return func(w http.ResponseWriter, r *http.Request) {
134134
if strings.EqualFold(r.URL.Path, "/api/version") && r.Method == http.MethodGet {
135-
_, err := w.Write([]byte(`{"major": "13", "minor":"6"}`))
135+
_, err := w.Write([]byte(`{"major": "14", "minor":"0"}`))
136136
require.NoError(t, err)
137137
} else {
138138
w.WriteHeader(http.StatusNotFound)
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright Mia srl
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package itd
17+
18+
import (
19+
"context"
20+
"encoding/json"
21+
"errors"
22+
"fmt"
23+
"io/fs"
24+
"path/filepath"
25+
26+
"github.com/mia-platform/miactl/internal/client"
27+
"github.com/mia-platform/miactl/internal/clioptions"
28+
"github.com/mia-platform/miactl/internal/encoding"
29+
"github.com/mia-platform/miactl/internal/files"
30+
itd "github.com/mia-platform/miactl/internal/resources/item-type-definition"
31+
"github.com/mia-platform/miactl/internal/util"
32+
"github.com/spf13/cobra"
33+
)
34+
35+
var (
36+
ErrInvalidFilePath = errors.New("invalid file path")
37+
ErrPathIsFolder = errors.New("path must be a file path, not a folder")
38+
ErrFileFormatNotSupported = errors.New("file format not supported, supported formats are: .json, .yaml, .yml")
39+
ErrResWithoutName = errors.New("name field is required")
40+
ErrResNameNotAString = errors.New("name field must be string")
41+
ErrPuttingResources = errors.New("cannot save the item type definition")
42+
)
43+
44+
const (
45+
putItdEndpoint = "/api/tenants/%s/marketplace/item-type-definitions/"
46+
47+
cmdPutLongDescription = ` Create or update an Item Type Definition. It works with Mia-Platform Console v14.1.0 or later.
48+
49+
You need to specify the flag --file or -f that accepts a file and companyId.
50+
51+
Supported formats are JSON (.json files) and YAML (.yaml or .yml files). The company-id flag can be omitted if it is already set in the context.`
52+
53+
putExample = `
54+
# Create the item type definition in file myFantasticGoTemplate.json located in the current directory
55+
miactl catalog apply --file myFantasticGoTemplate.json
56+
57+
# Create the item type definition in file myFantasticGoTemplate.json, with relative path
58+
miactl catalog apply --file ./path/to/myFantasticGoTemplate.json`
59+
60+
putCmdUse = "delete { --file file-path }"
61+
)
62+
63+
func PutCmd(options *clioptions.CLIOptions) *cobra.Command {
64+
cmd := &cobra.Command{
65+
Use: putCmdUse,
66+
Short: "Create or update an Item Type Definition",
67+
Long: cmdPutLongDescription,
68+
Example: putExample,
69+
RunE: func(cmd *cobra.Command, _ []string) error {
70+
restConfig, err := options.ToRESTConfig()
71+
cobra.CheckErr(err)
72+
client, err := client.APIClientForConfig(restConfig)
73+
cobra.CheckErr(err)
74+
75+
canUseNewAPI, versionError := util.VersionCheck(cmd.Context(), client, 14, 1)
76+
if !canUseNewAPI || versionError != nil {
77+
return itd.ErrUnsupportedCompanyVersion
78+
}
79+
80+
companyID := restConfig.CompanyID
81+
if len(companyID) == 0 {
82+
return itd.ErrMissingCompanyID
83+
}
84+
85+
outcome, err := putItemFromPath(
86+
cmd.Context(),
87+
client,
88+
companyID,
89+
options.ItemTypeDefinitionFilePath,
90+
options.OutputFormat,
91+
)
92+
cobra.CheckErr(err)
93+
94+
fmt.Println(outcome)
95+
96+
return nil
97+
},
98+
}
99+
100+
options.AddItemTypeDefinitionFileFlag(cmd)
101+
102+
return cmd
103+
}
104+
105+
func putItemFromPath(ctx context.Context, client *client.APIClient, companyID string, filePath string, outputFormat string) (string, error) {
106+
_, err := checkFilePath(filePath)
107+
if err != nil {
108+
return "", fmt.Errorf("%w: %s", err, err)
109+
}
110+
111+
outcome, err := putItemTypeDefinition(ctx, client, companyID, filePath)
112+
if err != nil {
113+
return "", fmt.Errorf("%w: %s", ErrPuttingResources, err)
114+
}
115+
116+
data, err := outcome.Marshal(outputFormat)
117+
if err != nil {
118+
return "", err
119+
}
120+
121+
return string(data), nil
122+
}
123+
124+
func checkFilePath(rootPath string) (string, error) {
125+
err := filepath.Walk(rootPath, func(path string, info fs.FileInfo, err error) error {
126+
if err != nil {
127+
return err
128+
}
129+
if info.IsDir() {
130+
return ErrPathIsFolder
131+
}
132+
extension := filepath.Ext(path)
133+
if extension == encoding.YmlExtension || extension == encoding.YamlExtension ||
134+
extension == encoding.JSONExtension {
135+
return nil
136+
}
137+
return ErrFileFormatNotSupported
138+
})
139+
if err != nil {
140+
return "", err
141+
}
142+
return rootPath, nil
143+
}
144+
145+
func putItemTypeDefinition(
146+
ctx context.Context,
147+
client *client.APIClient,
148+
companyID string,
149+
filePath string,
150+
) (*itd.GenericItemTypeDefinition, error) {
151+
if companyID == "" {
152+
return nil, itd.ErrMissingCompanyID
153+
}
154+
155+
itemTypeDefinition := &itd.GenericItemTypeDefinition{}
156+
if err := files.ReadFile(filePath, itemTypeDefinition); err != nil {
157+
return nil, err
158+
}
159+
160+
bodyBytes, err := json.Marshal(itemTypeDefinition)
161+
if err != nil {
162+
return nil, err
163+
}
164+
165+
resp, err := client.Put().
166+
APIPath(fmt.Sprintf(putItdEndpoint, companyID)).
167+
Body(bodyBytes).
168+
Do(ctx)
169+
if err != nil {
170+
return nil, err
171+
}
172+
if err := resp.Error(); err != nil {
173+
return nil, err
174+
}
175+
176+
putResponse := &itd.GenericItemTypeDefinition{}
177+
err = resp.ParseResponse(putResponse)
178+
if err != nil {
179+
return nil, err
180+
}
181+
182+
return putResponse, nil
183+
}

0 commit comments

Comments
 (0)