Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add set command #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions docs/ccli_manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ required yml file. For example:
```
$ ccli update openssl-1.1.1n.v4.yml
```
- **set** <file.yml> - sets the fields of a part record including empty values.
```
$ ccli set openssl-1.1.1n.v4.yml
```
- **upload** <source archive> - uploads the specified source archive. A a new part record will be created if it does not correspond part record exists otherwise
it will be associated with an existing part if it already exists.
```
Expand Down
13 changes: 6 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@ func init() {
viper.AddConfigPath(".")
// read the config file
if err := viper.ReadInConfig(); err != nil {
if err != nil {
if err != errors.New("open ccli_config.yml: no such file or directory") {
fmt.Println("User configuration file not found. Please create ccli_config.yml and copy the contents of ccli_config.DEFAULT.yml.")
} else {
fmt.Println("Error reading in config file. Error:", err)
}
os.Exit(1)
if err != errors.New("open ccli_config.yml: no such file or directory") {
fmt.Println("User configuration file not found. Please create ccli_config.yml and copy the contents of ccli_config.DEFAULT.yml.")
} else {
fmt.Println("Error reading in config file. Error:", err)
}
os.Exit(1)
}
// unmarshal the config file parameters to a struct
if err := viper.Unmarshal(&configFile); err != nil {
Expand Down Expand Up @@ -110,6 +108,7 @@ func main() {
rootCmd.AddCommand(cmd.Ping(&configFile))
rootCmd.AddCommand(cmd.Upload(&configFile))
rootCmd.AddCommand(cmd.Update(&configFile, client, indent))
rootCmd.AddCommand(cmd.Set(&configFile, client, indent))
rootCmd.AddCommand(cmd.Query(&configFile, client, indent))
rootCmd.AddCommand(cmd.Find(&configFile, client, indent))
rootCmd.AddCommand(cmd.Export(&configFile, client, indent))
Expand Down
18 changes: 18 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ func TestUpdate(tester *testing.T) {
}
}

// TestSet sets a part's information based on the yml file present in the given path using the
// command line and checks if the command line output is as expected
func TestSet(tester *testing.T) {
// ccli set testdir/yml/openid-client-4.9.1.yml
cmd := exec.Command("ccli", "set", "testdir/yml/openid-client-4.9.1.yml")
// capturing command line output
output, err := cmd.Output()
if err != nil {
tester.Error("failed to capture command line output", err)
}
// splitting and extracting the output message to be checked
result := strings.Split(string(output), "\n")[0]
expected := "Part fields successfully set"
if result != expected {
tester.Errorf("Expected %s but got %s", expected, result)
}
}

// TestAddLicenseProfile adds a part's licensing profile based on the yml file present in the given path using the
// command line and checks if the command line output is as expected
func TestAddLicenseProfile(tester *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions packages/cmd/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func Example() *cobra.Command {
$ ccli export part id sdl3ga-naTs42g5-rbow2A -o file.yml
$ ccli export template security -o file.yml
$ ccli update openssl-1.1.1n.v4.yml
$ ccli set openssl-1.1.1n.v4.yml
$ ccli upload openssl-1.1.1n.tar.gz
$ ccli find part busybox
$ ccli find sha256 2493347f59c03...
Expand Down
90 changes: 90 additions & 0 deletions packages/cmd/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2020 Wind River Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
// OR CONDITIONS OF ANY KIND, either express or implied.
package cmd

import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"os"
"wrs/catalog/ccli/packages/config"
"wrs/catalog/ccli/packages/graphql"
"wrs/catalog/ccli/packages/yaml"

graph "github.com/hasura/go-graphql-client"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

// Set() is a sub command responsible for setting part information including zero values
// based on a given yml file.
func Set(configFile *config.ConfigData, client *graph.Client, indent string) *cobra.Command {
// cobra command for set part
setCmd := &cobra.Command{
Use: "set [path]",
Short: "Set the fields of a part in the Software Parts Catalog including empty values",
// function to be run as setup for the command execution
PreRunE: func(cmd *cobra.Command, args []string) error {
// check if exactly 1 argument is present
if len(args) < 1 {
return errors.New("No path provided.")
}
return nil
},
// function to be run during command execution
RunE: func(cmd *cobra.Command, args []string) error {
argImportPath := args[0]
if argImportPath == "" {
return errors.New("error setting part fields, set subcommand usage: ./ccli set <Path>")
}
// check if the file is of yaml/yml format
if argImportPath != "" {
if argImportPath[len(argImportPath)-5:] != ".yaml" && argImportPath[len(argImportPath)-4:] != ".yml" {
return errors.New("error importing part, import path not a yaml file")
}
// open the file
f, err := os.Open(argImportPath)
if err != nil {
return errors.Wrapf(err, "error opening file")
}
defer f.Close()
// read all the data from the file
data, err := io.ReadAll(f)
if err != nil {
return errors.Wrapf(err, "error reading file")
}
// unmarshal the data of the file into a struct
var partData yaml.Part
if err = yaml.Unmarshal(data, &partData); err != nil {
return errors.Wrapf(err, "error decoding file contents")
}
slog.Debug("setting part fields")
// set the part fields with the given part data
returnPart, err := graphql.SetPart(context.Background(), client, &partData)
if err != nil {
return errors.Wrapf(err, "error setting part fields")
}
// marshal the struct into a json
prettyJson, err := json.MarshalIndent(&returnPart, "", indent)
if err != nil {
return errors.Wrapf(err, "error prettifying json")
}
fmt.Printf("Part fields successfully set\n%s\n", string(prettyJson))
}
return nil
},
}
return setCmd

}
66 changes: 66 additions & 0 deletions packages/graphql/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,72 @@ func UpdatePart(ctx context.Context, client *graphql.Client, partData *yaml.Part
return &mutation.Part, nil
}

// Sets the fields of a part record including emptry values from the catalog using yaml template
func SetPart(ctx context.Context, client *graphql.Client, partData *yaml.Part) (*Part, error) {

var partInput PartInput
if partData.CatalogID == "" {
if partData.FVC == "" && partData.Sha256 == "" {
return nil, errors.New("error updating part, no part identifier provided")
}
if partData.FVC != "" {
catalogID, err := GetPartIDByFVC(ctx, client, partData.FVC)
if err != nil {
return nil, err
}
if catalogID != nil {
partInputID := UUID(catalogID.String())
partInput.ID = &partInputID
}
} else if partData.Sha256 != "" {
catalogID, err := GetPartIDBySha256(ctx, client, partData.Sha256)
if err != nil {
return nil, err
}
if catalogID != nil {
partInputID := UUID(catalogID.String())
partInput.ID = &partInputID
}
}
}

if partData.ComprisedOf != "" {
comprisedID := UUID(partData.ComprisedOf)
partInput.Comprised = &comprisedID
}

var mutation struct {
Part `graphql:"setPart(partInput: $partInput)"`
}

variables := map[string]interface{}{
"partInput": partInput,
}

if err := client.Mutate(ctx, &mutation, variables); err != nil {
return nil, err
}

if partData.Aliases != nil {
var aliasMutation struct {
UUID `graphql:"createAlias(id: $id, alias: $alias)"`
}

for _, v := range partData.Aliases {
aliasVariables := map[string]interface{}{
"id": *partInput.ID,
"alias": v,
}

if err := client.Mutate(ctx, &aliasMutation, aliasVariables); err != nil {
return nil, err
}
}
}

return &mutation.Part, nil
}

// Used to convert a part data structure into the structure expected by yaml i/o
func UnmarshalPart(part *Part, yamlPart *yaml.Part) error {
yamlPart.Format = 1.0
Expand Down