diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index e594e363..cf53ad81 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -229,6 +229,14 @@ The `galasactl` tool can generate the following errors: - GAL1230E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' - GAL1231E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are: '{}' - GAL1232E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in the json format. +- GAL1233E: The stream name provided by the --name field cannot be an empty string. +- GAL1234E: Could not get list of test streams from API server. Reason: '{}'. +- GAL1235E: The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore). +- GAL1236E: Failed to get streams. Unexpected http status code {} received from the server. +- GAL1237E: Failed to get streams. Unexpected http status code {} received from the server. Error details from the server could not be read. Cause: {} +- GAL1238E: Failed to get streams. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' +- GAL1239E: Failed to get streams. Unexpected http status code {} received from the server. Error details from the server are: '{}' +- GAL1240E: Failed to get streams. Unexpected http status code {} received from the server. Error details from the server are not in the json format. - GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '{}', and 'pre-release' repository is '{}' - GAL2501I: Downloaded {} artifacts to folder '{}' diff --git a/docs/generated/galasactl.md b/docs/generated/galasactl.md index 139f2054..f1d32b53 100644 --- a/docs/generated/galasactl.md +++ b/docs/generated/galasactl.md @@ -25,5 +25,6 @@ A tool for controlling Galasa resources using the command-line. * [galasactl roles](galasactl_roles.md) - Manage roles stored in the Galasa service * [galasactl runs](galasactl_runs.md) - Manage test runs in the ecosystem * [galasactl secrets](galasactl_secrets.md) - Manage secrets stored in the Galasa service's credentials store +* [galasactl streams](galasactl_streams.md) - Manages test streams in a Galasa service * [galasactl users](galasactl_users.md) - Manages users in an ecosystem diff --git a/docs/generated/galasactl_streams.md b/docs/generated/galasactl_streams.md new file mode 100644 index 00000000..d6b0002d --- /dev/null +++ b/docs/generated/galasactl_streams.md @@ -0,0 +1,29 @@ +## galasactl streams + +Manages test streams in a Galasa service + +### Synopsis + +Parent command for managing test streams in a Galasa service + +### Options + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + -h, --help Displays the options for the 'streams' command. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### Options inherited from parent commands + +``` + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. +``` + +### SEE ALSO + +* [galasactl](galasactl.md) - CLI for Galasa +* [galasactl streams get](galasactl_streams_get.md) - Gets a list of test streams + diff --git a/docs/generated/galasactl_streams_get.md b/docs/generated/galasactl_streams_get.md new file mode 100644 index 00000000..3ce86520 --- /dev/null +++ b/docs/generated/galasactl_streams_get.md @@ -0,0 +1,34 @@ +## galasactl streams get + +Gets a list of test streams + +### Synopsis + +Get a list of test streams from the Galasa service + +``` +galasactl streams get [flags] +``` + +### Options + +``` + --format string the output format of the returned streams. Supported formats are: 'summary', 'yaml'. (default "summary") + -h, --help Displays the options for the 'streams get' command. + --name string An optional field indicating the name of a test stream +``` + +### Options inherited from parent commands + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### SEE ALSO + +* [galasactl streams](galasactl_streams.md) - Manages test streams in a Galasa service + diff --git a/pkg/cmd/commandCollection.go b/pkg/cmd/commandCollection.go index 743bc071..0522ce55 100644 --- a/pkg/cmd/commandCollection.go +++ b/pkg/cmd/commandCollection.go @@ -72,6 +72,8 @@ const ( COMMAND_NAME_USERS_DELETE = "users delete" COMMAND_NAME_ROLES = "roles" COMMAND_NAME_ROLES_GET = "roles get" + COMMAND_NAME_STREAMS = "streams" + COMMAND_NAME_STREAMS_GET = "streams get" ) // ----------------------------------------------------------------- @@ -172,6 +174,10 @@ func (commands *commandCollectionImpl) init(factory spi.Factory) error { err = commands.addRolesCommands(factory, rootCommand, commsFlagSet) } + if err == nil { + err = commands.addStreamsCommands(factory, rootCommand, commsFlagSet) + } + if err == nil { commands.setHelpFlags() } @@ -508,6 +514,26 @@ func (commands *commandCollectionImpl) addRolesCommands(factory spi.Factory, roo return err } +func (commands *commandCollectionImpl) addStreamsCommands(factory spi.Factory, rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + + var err error + var streamsCommand spi.GalasaCommand + var streamsGetCommand spi.GalasaCommand + + streamsCommand, err = NewStreamsCommand(rootCommand, commsFlagSet) + + if err == nil { + streamsGetCommand, err = NewStreamsGetCommand(factory, streamsCommand, commsFlagSet) + if err == nil { + commands.commandMap[streamsCommand.Name()] = streamsCommand + commands.commandMap[streamsGetCommand.Name()] = streamsGetCommand + } + } + + return err + +} + func (commands *commandCollectionImpl) setHelpFlags() { for _, command := range commands.commandMap { command.CobraCommand().Flags().BoolP("help", "h", false, "Displays the options for the '"+command.Name()+"' command.") diff --git a/pkg/cmd/streams.go b/pkg/cmd/streams.go new file mode 100644 index 00000000..1d426b76 --- /dev/null +++ b/pkg/cmd/streams.go @@ -0,0 +1,98 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package cmd + +import ( + "github.com/galasa-dev/cli/pkg/spi" + "github.com/spf13/cobra" +) + +type StreamsCmdValues struct { + name string +} + +type StreamsCommand struct { + values *StreamsCmdValues + cobraCommand *cobra.Command +} + +// ------------------------------------------------------------------------------------------------ +// Constructors methods +// ------------------------------------------------------------------------------------------------ +func NewStreamsCommand(rootCmd spi.GalasaCommand, commsFlagSet GalasaFlagSet) (spi.GalasaCommand, error) { + + cmd := new(StreamsCommand) + err := cmd.init(rootCmd, commsFlagSet) + return cmd, err + +} + +// ------------------------------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------------------------------ +func (cmd *StreamsCommand) Name() string { + return COMMAND_NAME_STREAMS +} + +func (cmd *StreamsCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *StreamsCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private methods +// ------------------------------------------------------------------------------------------------ + +func (cmd *StreamsCommand) init(rootCmd spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + + var err error + cmd.values = &StreamsCmdValues{} + cmd.cobraCommand = cmd.createCobraCommand(rootCmd, commsFlagSet) + + return err + +} + +func (cmd *StreamsCommand) createCobraCommand( + rootCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) *cobra.Command { + + streamsCobraCmd := &cobra.Command{ + Use: "streams", + Short: "Manages test streams in a Galasa service", + Long: "Parent command for managing test streams in a Galasa service", + } + + streamsCobraCmd.PersistentFlags().AddFlagSet(commsFlagSet.Flags()) + rootCommand.CobraCommand().AddCommand(streamsCobraCmd) + + return streamsCobraCmd + +} + +func addStreamNameFlag(cmd *cobra.Command, isMandatory bool, streamCmdValues *StreamsCmdValues) { + + flagName := "name" + var description string + + if isMandatory { + description = "A mandatory field indicating the name of a test stream." + } else { + description = "An optional field indicating the name of a test stream" + } + + cmd.Flags().StringVar(&streamCmdValues.name, flagName, "", description) + + if isMandatory { + cmd.MarkFlagRequired(flagName) + } + +} diff --git a/pkg/cmd/streamsGet.go b/pkg/cmd/streamsGet.go new file mode 100644 index 00000000..9daf85bb --- /dev/null +++ b/pkg/cmd/streamsGet.go @@ -0,0 +1,159 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package cmd + +import ( + "log" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/spi" + "github.com/galasa-dev/cli/pkg/streams" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/spf13/cobra" +) + +type StreamsGetCmdValues struct { + outputFormat string +} + +// Objective: Allow user to do this: +// +// streams get +type StreamsGetCommand struct { + cobraCommand *cobra.Command + values *StreamsGetCmdValues +} + +// ------------------------------------------------------------------------------------------------ +// Constructors methods +// ------------------------------------------------------------------------------------------------ +func NewStreamsGetCommand( + factory spi.Factory, + streamsGetCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) (spi.GalasaCommand, error) { + + cmd := new(StreamsGetCommand) + err := cmd.init(factory, streamsGetCommand, commsFlagSet) + return cmd, err + +} + +// ------------------------------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------------------------------ +func (cmd *StreamsGetCommand) Name() string { + return COMMAND_NAME_STREAMS_GET +} + +func (cmd *StreamsGetCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *StreamsGetCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private methods +// ------------------------------------------------------------------------------------------------ +func (cmd *StreamsGetCommand) init(factory spi.Factory, streamsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + + var err error + cmd.values = &StreamsGetCmdValues{} + cmd.cobraCommand, err = cmd.createCobraCmd(factory, streamsCommand, commsFlagSet) + return err + +} + +func (cmd *StreamsGetCommand) createCobraCmd( + factory spi.Factory, + streamsCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) (*cobra.Command, error) { + + var err error + + commsFlagSetValues := commsFlagSet.Values().(*CommsFlagSetValues) + streamCommandValues := streamsCommand.Values().(*StreamsCmdValues) + + streamsGetCobraCmd := &cobra.Command{ + Use: "get", + Short: "Gets a list of test streams", + Long: "Get a list of test streams from the Galasa service", + Aliases: []string{COMMAND_NAME_STREAMS_GET}, + RunE: func(cobraCommand *cobra.Command, args []string) error { + return cmd.executeStreamsGet( + factory, streamsCommand.Values().(*StreamsCmdValues), commsFlagSetValues, + ) + }, + } + + addStreamNameFlag(streamsGetCobraCmd, false, streamCommandValues) + + formatters := streams.GetFormatterNamesAsString() + streamsGetCobraCmd.Flags().StringVar(&cmd.values.outputFormat, "format", "summary", "the output format of the returned streams. Supported formats are: "+formatters+".") + + streamsCommand.CobraCommand().AddCommand(streamsGetCobraCmd) + + return streamsGetCobraCmd, err + +} + +func (cmd *StreamsGetCommand) executeStreamsGet( + factory spi.Factory, + streamsCmdValues *StreamsCmdValues, + commsFlagSetValues *CommsFlagSetValues, +) error { + + var err error + + // Operations on the file system will all be relative to the current folder. + fileSystem := factory.GetFileSystem() + + err = utils.CaptureLog(fileSystem, commsFlagSetValues.logFileName) + if err == nil { + + commsFlagSetValues.isCapturingLogs = true + log.Println("Galasa CLI - Get streams from the Galasa service") + + env := factory.GetEnvironment() + + var galasaHome spi.GalasaHome + galasaHome, err = utils.NewGalasaHome(fileSystem, env, commsFlagSetValues.CmdParamGalasaHomePath) + + if err == nil { + + var commsClient api.APICommsClient + commsClient, err = api.NewAPICommsClient( + commsFlagSetValues.bootstrap, + commsFlagSetValues.maxRetries, + commsFlagSetValues.retryBackoffSeconds, + factory, + galasaHome, + ) + + if err == nil { + + var console = factory.GetStdOutConsole() + var byteReader = factory.GetByteReader() + + getStreamsFunc := func(apiClient *galasaapi.APIClient) error { + return streams.GetStreams(streamsCmdValues.name, cmd.values.outputFormat, apiClient, console,byteReader) + } + + err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(getStreamsFunc) + + } + + } + + } + + return err +} diff --git a/pkg/cmd/streamsGet_test.go b/pkg/cmd/streamsGet_test.go new file mode 100644 index 00000000..def3ff47 --- /dev/null +++ b/pkg/cmd/streamsGet_test.go @@ -0,0 +1,105 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestStreamsGetCommandInCommandCollectionHasName(t *testing.T) { + + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + StreamsGetCommand, err := commands.GetCommand(COMMAND_NAME_STREAMS_GET) + assert.Nil(t, err) + + assert.Equal(t, COMMAND_NAME_STREAMS_GET, StreamsGetCommand.Name()) + assert.NotNil(t, StreamsGetCommand.CobraCommand()) +} + +func TestStreamsGetHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + + var args []string = []string{"streams", "get", "--help"} + + // When... + err := Execute(factory, args) + + // Then... + // Check what the user saw is reasonable. + checkOutput("Displays the options for the 'streams get' command.", "", factory, t) + + assert.Nil(t, err) +} + +func TestStreamsGetNameFlagsReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_STREAMS_GET, factory, t) + + var args []string = []string{"streams", "get", "--name", "mystream"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw was reasonable + checkOutput("", "", factory, t) + + parentCmd, err := commandCollection.GetCommand(COMMAND_NAME_STREAMS) + assert.Nil(t, err) + assert.Contains(t, parentCmd.Values().(*StreamsCmdValues).name, "mystream") +} + +func TestStreamsGetFormatFlagYamlValueReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_STREAMS_GET, factory, t) + + var args []string = []string{"streams", "get", "--name", "mystream", "--format", "yaml"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw was reasonable + checkOutput("", "", factory, t) + + parentCmd, err := commandCollection.GetCommand(COMMAND_NAME_STREAMS) + assert.Nil(t, err) + assert.Contains(t, parentCmd.Values().(*StreamsCmdValues).name, "mystream") +} + +func TestStreamsGetFormatFlagYamlSummaryReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_STREAMS_GET, factory, t) + + var args []string = []string{"streams", "get", "--name", "mystream", "--format", "summary"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw was reasonable + checkOutput("", "", factory, t) + + parentCmd, err := commandCollection.GetCommand(COMMAND_NAME_STREAMS) + assert.Nil(t, err) + assert.Contains(t, parentCmd.Values().(*StreamsCmdValues).name, "mystream") +} diff --git a/pkg/cmd/streams_test.go b/pkg/cmd/streams_test.go new file mode 100644 index 00000000..a778d9d4 --- /dev/null +++ b/pkg/cmd/streams_test.go @@ -0,0 +1,59 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestStreamsCommandInCommandCollection(t *testing.T) { + + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + streamsCommand, err := commands.GetCommand(COMMAND_NAME_STREAMS) + assert.Nil(t, err) + + assert.NotNil(t, streamsCommand) + assert.Equal(t, COMMAND_NAME_STREAMS, streamsCommand.Name()) + assert.NotNil(t, streamsCommand.Values()) + assert.IsType(t, &StreamsCmdValues{}, streamsCommand.Values()) + assert.NotNil(t, streamsCommand.CobraCommand()) +} + +func TestStreamsHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + + var args []string = []string{"streams", "--help"} + + // When... + err := Execute(factory, args) + + // Then... + + // Check what the user saw is reasonable. + checkOutput("Displays the options for the 'streams' command.", "", factory, t) + + assert.Nil(t, err) +} + +func TestStreamsNoCommandsProducesUsageReport(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + var args []string = []string{"streams"} + + // When... + err := Execute(factory, args) + + // Then... + assert.Nil(t, err) + // Check what the user saw was reasonable + checkOutput("Usage:\n galasactl streams [command]", "", factory, t) +} diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index 8355c986..1321382a 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -16,11 +16,11 @@ import ( var ( RATE_LIMIT_STATUS_CODES_MAP = map[int]struct{}{ http.StatusServiceUnavailable: {}, - http.StatusTooManyRequests: {}, - http.StatusUnauthorized: {}, + http.StatusTooManyRequests: {}, + http.StatusUnauthorized: {}, } - AUTH_STATUS_CODES_MAP = map[int]struct{} { + AUTH_STATUS_CODES_MAP = map[int]struct{}{ http.StatusUnauthorized: {}, } ) @@ -419,6 +419,15 @@ var ( GALASA_ERROR_UPDATE_USER_SERVER_REPORTED_ERROR = NewMessageType("GAL1216E: An attempt to update a user '%s' failed. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1216, STACK_TRACE_NOT_WANTED) GALASA_ERROR_UPDATE_USER_EXPLANATION_NOT_JSON = NewMessageType("GAL1217E: An attempt to update a user '%s' failed. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1217, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_MISSING_STREAM_NAME_FLAG = NewMessageType("GAL1233E: The stream name provided by the --name field cannot be an empty string.", 1233, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_RETRIEVING_STREAMS_FROM_API_SERVER = NewMessageType("GAL1234E: Could not get list of test streams from API server. Reason: '%s'.", 1234, STACK_TRACE_WANTED) + GALASA_ERROR_INVALID_STREAM_NAME = NewMessageType("GAL1235E: The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore).", 1235, STACK_TRACE_WANTED) + GALASA_ERROR_GET_STREAMS_NO_RESPONSE_CONTENT = NewMessageType("GAL1236E: Failed to get streams. Unexpected http status code %v received from the server.", 1236, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_GET_STREAMS_RESPONSE_BODY_UNREADABLE = NewMessageType("GAL1237E: Failed to get streams. Unexpected http status code %v received from the server. Error details from the server could not be read. Cause: %s", 1237, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_GET_STREAMS_UNPARSEABLE_CONTENT = NewMessageType("GAL1238E: Failed to get streams. Unexpected http status code %v received from the server. Error details from the server are not in a valid json format. Cause: '%s'", 1238, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_GET_STREAMS_SERVER_REPORTED_ERROR = NewMessageType("GAL1239E: Failed to get streams. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1239, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_GET_STREAMS_EXPLANATION_NOT_JSON = NewMessageType("GAL1240E: Failed to get streams. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1240, STACK_TRACE_NOT_WANTED) + // When getting multiple monitors... GALASA_ERROR_GET_MONITORS_REQUEST_FAILED = NewMessageType("GAL1218E: Failed to get monitors. Sending the get request to the Galasa service failed. Cause is %v", 1218, STACK_TRACE_NOT_WANTED) GALASA_ERROR_GET_MONITORS_NO_RESPONSE_CONTENT = NewMessageType("GAL1219E: Failed to get monitors. Unexpected http status code %v received from the server.", 1219, STACK_TRACE_NOT_WANTED) @@ -430,7 +439,7 @@ var ( // Getting a single monitor by name... GALASA_ERROR_MONITOR_NAME_NOT_FOUND = NewMessageType("GAL1224E: Galasa Monitor named %v is not known on the Galasa service.", 1224, STACK_TRACE_NOT_WANTED) GALASA_ERROR_INVALID_MONITOR_NAME = NewMessageType("GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore).", 1225, STACK_TRACE_NOT_WANTED) - + // When updating a monitor... GALASA_ERROR_INVALID_IS_ENABLED_FLAG = NewMessageType("GAL1226E: Invalid '--is-enabled' value provided. Supported values are 'true' and 'false'. Check your command parameters and try again.", 1226, STACK_TRACE_NOT_WANTED) GALASA_ERROR_UPDATE_MONITOR_REQUEST_FAILED = NewMessageType("GAL1227E: Failed to update a monitor named '%s'. Sending the put request to the Galasa service failed. Cause is %v", 1227, STACK_TRACE_NOT_WANTED) diff --git a/pkg/streams/streams.go b/pkg/streams/streams.go new file mode 100644 index 00000000..e645fd4b --- /dev/null +++ b/pkg/streams/streams.go @@ -0,0 +1,31 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streams + +import ( + "strings" + + galasaErrors "github.com/galasa-dev/cli/pkg/errors" + "github.com/galasa-dev/cli/pkg/utils" +) + +func validateStreamName(streamName string) (string, error) { + + var err error + streamName = strings.TrimSpace(streamName) + + if streamName == "" { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_MISSING_STREAM_NAME_FLAG) + } else { + if !utils.IsNameValid(streamName) { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_STREAM_NAME) + } + } + + return streamName, err + +} diff --git a/pkg/streams/streamsGet.go b/pkg/streams/streamsGet.go new file mode 100644 index 00000000..12694fa4 --- /dev/null +++ b/pkg/streams/streamsGet.go @@ -0,0 +1,216 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streams + +import ( + "context" + "log" + "net/http" + "sort" + "strings" + + "github.com/galasa-dev/cli/pkg/embedded" + galasaErrors "github.com/galasa-dev/cli/pkg/errors" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/spi" + "github.com/galasa-dev/cli/pkg/streamsformatter" +) + +var ( + formatters = createFormatters() +) + +func GetStreams(streamName string, format string, apiClient *galasaapi.APIClient, console spi.Console, byteReader spi.ByteReader) error { + + var streamData []galasaapi.Stream + + chosenFormatter, err := validateFormatFlag(format) + + if err == nil { + if streamName != "" { + streamName, err = validateStreamName(streamName) + } + + if err == nil { + streamData, err = getStreamsFromRestApi(streamName, apiClient, byteReader) + if err == nil { + err = formatFetchedStreamsAndWriteToConsole(streamData, console, chosenFormatter) + } + } + } + + return err + +} + +func formatFetchedStreamsAndWriteToConsole(streams []galasaapi.Stream, console spi.Console, formatter streamsformatter.StreamsFormatter) error { + + formattedOutput, err := formatter.FormatStreams(streams) + if err == nil { + console.WriteString(formattedOutput) + } + + return err + +} + +func getStreamsFromRestApi( + streamName string, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) ([]galasaapi.Stream, error) { + + var streams []galasaapi.Stream + var err error + var restApiVersion string + + restApiVersion, err = embedded.GetGalasactlRestApiVersion() + + if err == nil { + if streamName != "" { + streams, err = getStreamByName(streamName, apiClient, restApiVersion, byteReader) + } else { + streams, err = getAllStreams(apiClient, restApiVersion, byteReader) + } + } + + return streams, err + +} + +func createFormatters() map[string]streamsformatter.StreamsFormatter { + + formatters := make(map[string]streamsformatter.StreamsFormatter, 0) + summaryFormatter := streamsformatter.NewStreamsSummaryFormatter() + yamlFormatter := streamsformatter.NewStreamsYamlFormatter() + + formatters[summaryFormatter.GetName()] = summaryFormatter + formatters[yamlFormatter.GetName()] = yamlFormatter + + return formatters + +} + +func GetFormatterNamesAsString() string { + names := make([]string, 0, len(formatters)) + for name := range formatters { + names = append(names, name) + } + sort.Strings(names) + formatterNames := strings.Builder{} + + for index, formatterName := range names { + + if index != 0 { + formatterNames.WriteString(", ") + } + formatterNames.WriteString("'" + formatterName + "'") + } + + return formatterNames.String() +} + +func getStreamByName( + streamName string, + apiClient *galasaapi.APIClient, + restApiVersion string, + byteReader spi.ByteReader, +) ([]galasaapi.Stream, error) { + + var err error + var streamIn *galasaapi.Stream + var resp *http.Response + var context context.Context = nil + var streamsToReturn []galasaapi.Stream + + apiCall := apiClient.StreamsAPIApi.GetStreamByName(context, streamName).ClientApiVersion(restApiVersion) + + streamIn, resp, err = apiCall.Execute() + + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + + if resp == nil { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_RETRIEVING_STREAMS_FROM_API_SERVER, err.Error()) + } else { + err = galasaErrors.HttpResponseToGalasaError( + resp, + "", + byteReader, + galasaErrors.GALASA_ERROR_GET_STREAMS_NO_RESPONSE_CONTENT, + galasaErrors.GALASA_ERROR_GET_STREAMS_RESPONSE_BODY_UNREADABLE, + galasaErrors.GALASA_ERROR_GET_STREAMS_UNPARSEABLE_CONTENT, + galasaErrors.GALASA_ERROR_GET_STREAMS_SERVER_REPORTED_ERROR, + galasaErrors.GALASA_ERROR_GET_STREAMS_EXPLANATION_NOT_JSON, + ) + log.Println("getStreamsFromRestApi - Failed to retrieve list of test streams from API server") + } + + } + + streamsToReturn = []galasaapi.Stream{*streamIn} + return streamsToReturn, err + +} + +func getAllStreams( + apiClient *galasaapi.APIClient, + restApiVersion string, + byteReader spi.ByteReader, +) ([]galasaapi.Stream, error) { + + var err error + var streams []galasaapi.Stream + var resp *http.Response + var context context.Context = nil + + apiCall := apiClient.StreamsAPIApi.GetStreams(context).ClientApiVersion(restApiVersion) + + streams, resp, err = apiCall.Execute() + + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + + if resp == nil { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_RETRIEVING_STREAMS_FROM_API_SERVER, err.Error()) + } else { + err = galasaErrors.HttpResponseToGalasaError( + resp, + "", + byteReader, + galasaErrors.GALASA_ERROR_GET_STREAMS_NO_RESPONSE_CONTENT, + galasaErrors.GALASA_ERROR_GET_STREAMS_RESPONSE_BODY_UNREADABLE, + galasaErrors.GALASA_ERROR_GET_STREAMS_UNPARSEABLE_CONTENT, + galasaErrors.GALASA_ERROR_GET_STREAMS_SERVER_REPORTED_ERROR, + galasaErrors.GALASA_ERROR_GET_STREAMS_EXPLANATION_NOT_JSON, + ) + } + log.Println("getStreamsFromRestApi - Failed to retrieve list of test streams from API server") + + } + + return streams, err +} + +func validateFormatFlag(outputFormatString string) (streamsformatter.StreamsFormatter, error) { + var err error + + chosenFormatter, isPresent := formatters[outputFormatString] + + if !isPresent { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_OUTPUT_FORMAT, outputFormatString, GetFormatterNamesAsString()) + } + + return chosenFormatter, err + +} diff --git a/pkg/streams/streamsGet_test.go b/pkg/streams/streamsGet_test.go new file mode 100644 index 00000000..40f14ab4 --- /dev/null +++ b/pkg/streams/streamsGet_test.go @@ -0,0 +1,269 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streams + +import ( + "net/http" + "testing" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestMultipleStreamsGetFormatsResultsOk(t *testing.T) { + + //Given.. + + body := ` +[ + { + "apiVersion": "galasa-dev/v1alpha1", + "kind": "GalasaStream", + "metadata": { + "name": "mystream", + "url": "http://localhost:8080/api/streams/myStream", + "description": "A stream which I use to..." + }, + "data": { + "isEnabled": true, + "repository": { + "url": "http://mymavenrepo.host/testmaterial" + }, + "obrs": [ + { + "group-id": "com.ibm.zosadk.k8s", + "artifact-id": "com.ibm.zosadk.k8s.obr", + "version": "0.1.0-SNAPSHOT" + } + ], + "testCatalog": { + "url": "http://mymavenrepo.host/testmaterial/com.ibm.zosadk.k8s/com.ibm.zosadk.k8s.obr/0.1.0-SNAPSHOT/testcatalog.yaml" + } + } + }, + { + "apiVersion": "galasa-dev/v1alpha1", + "kind": "GalasaStream", + "metadata": { + "name": "mystream2", + "url": "http://localhost:8080/api/streams/myStream", + "description": "Another stream to..." + }, + "data": { + "isEnabled": true, + "repository": { + "url": "http://mymavenrepo.host/testmaterial" + }, + "obrs": [ + { + "group-id": "com.ibm.zosadk.k8s", + "artifact-id": "com.ibm.zosadk.k8s.obr", + "version": "0.1.0-SNAPSHOT" + } + ], + "testCatalog": { + "url": "http://mymavenrepo.host/testmaterial/com.ibm.zosadk.k8s/com.ibm.zosadk.k8s.obr/0.1.0-SNAPSHOT/testcatalog.yaml" + } + } + } + +] +` + + getStreamsInteraction := utils.NewHttpInteraction("/streams", http.MethodGet) + getStreamsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.Header().Set("ClientApiVersion", "myVersion") + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(body)) + } + + mockByteReader := utils.NewMockByteReader() + + interactions := []utils.HttpInteraction{ + getStreamsInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiClient := api.InitialiseAPI(server.Server.URL) + console := utils.NewMockConsole() + + expectedOutput := `name state description +mystream enabled A stream which I use to... +mystream2 enabled Another stream to... + +Total:2 +` + + err := GetStreams("", "summary", apiClient, console, mockByteReader) + + assert.Nil(t, err) + assert.Equal(t, expectedOutput, console.ReadText()) + +} + +func TestMissingStreamNameFlagReturnsBadRequest(t *testing.T) { + //Given... + + body := `{"error_code": 1233,"error_message": "GAL1233E: The stream name provided by the --name field cannot be an empty string."}` + + getStreamsInteraction := utils.NewHttpInteraction("/streams", http.MethodGet) + getStreamsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.Header().Set("ClientApiVersion", "myVersion") + writer.WriteHeader(http.StatusBadRequest) // It's going to fail with an error on purpose ! + writer.Write([]byte(body)) + } + mockByteReader := utils.NewMockByteReader() + + interactions := []utils.HttpInteraction{ + getStreamsInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiClient := api.InitialiseAPI(server.Server.URL) + + console := utils.NewMockConsole() + expectedOutput := `GAL1233E: The stream name provided by the --name field cannot be an empty string.` + + //When + err := GetStreams(" ", "summary", apiClient, console, mockByteReader) + + //Then + assert.NotNil(t, err) + assert.Equal(t, expectedOutput, err.Error()) +} + +func TestMultipleStreamByNameGetFormatsResultsOk(t *testing.T) { + + //Given.. + var streamName = "mystream" + + body := ` +{ + "apiVersion": "galasa-dev/v1alpha1", + "kind": "GalasaStream", + "metadata": { + "name": "mystream", + "url": "http://localhost:8080/api/streams/myStream", + "description": "A stream which I use to..." + }, + "data": { + "isEnabled": true, + "repository": { + "url": "http://mymavenrepo.host/testmaterial" + }, + "obrs": [ + { + "groupId": "com.ibm.zosadk.k8s", + "artifactId": "com.ibm.zosadk.k8s.obr", + "version": "0.1.0-SNAPSHOT" + } + ], + "testCatalog": { + "url": "http://mymavenrepo.host/testmaterial/com.ibm.zosadk.k8s/com.ibm.zosadk.k8s.obr/0.1.0-SNAPSHOT/testcatalog.yaml" + } + } +} +` + + getStreamsInteraction := utils.NewHttpInteraction("/streams/"+streamName, http.MethodGet) + getStreamsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.Header().Set("ClientApiVersion", "myVersion") + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(body)) + } + mockByteReader := utils.NewMockByteReader() + + interactions := []utils.HttpInteraction{ + getStreamsInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiClient := api.InitialiseAPI(server.Server.URL) + console := utils.NewMockConsole() + + expectedOutput := `name state description +mystream enabled A stream which I use to... + +Total:1 +` + + err := GetStreams(streamName, "summary", apiClient, console, mockByteReader) + + assert.Nil(t, err) + assert.Equal(t, expectedOutput, console.ReadText()) + +} + +func TestTryGettingAnythingWithAnInvalidFormatterNameFailsImmediately(t *testing.T) { + // Not expecting any iteractions... + interactions := []utils.HttpInteraction{} + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + console := utils.NewMockConsole() + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + err := GetStreams( + "mystream", + "asdfghjk", + apiClient, + console, + mockByteReader) + + assert.NotNil(t, err, "Expected an error, didn't get one!") + assert.Contains(t, err.Error(), "GAL1067E") +} + +func TestCanGetAStreamByNameWhenStreamExistsGivesUnexpectedErrorSummaryFormat(t *testing.T) { + // Given... + outputFormat := "summary" + + // Create the expected HTTP interactions with the API server. + getStreamInteraction := utils.NewHttpInteraction("/streams", http.MethodGet) + getStreamInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + writer.Write([]byte("Not valid json format output from fake service")) + } + + interactions := []utils.HttpInteraction{ + getStreamInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + console := utils.NewMockConsole() + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := GetStreams( + "", + outputFormat, + apiClient, + console, + mockByteReader) + + // Then... + assert.NotNil(t, err, "GetStreams returned an error when none was expected") + assert.Contains(t, err.Error(), "GAL1238E") +} diff --git a/pkg/streams/streams_test.go b/pkg/streams/streams_test.go new file mode 100644 index 00000000..10e3c8af --- /dev/null +++ b/pkg/streams/streams_test.go @@ -0,0 +1,54 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streams + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateStreamNameFlagReturnsError_EmptyOrNullValue(t *testing.T) { + + invalidStreamName := "" + + streamName, err := validateStreamName(invalidStreamName) + + assert.NotNil(t, err) + assert.Empty(t, streamName) +} + +func TestValidateStreamNameRegexFlagReturnsError(t *testing.T) { + + invalidStreamName := "my.stream" + + streamName, err := validateStreamName(invalidStreamName) + + assert.NotNil(t, err) + assert.ErrorContains(t, err, "GAL1235E") + assert.NotEmpty(t, streamName) +} + +func TestValidateStreamNameRegexWithUnderScoreFlagReturnsNoError(t *testing.T) { + + validStreamName := "my_stream" + + streamName, err := validateStreamName(validStreamName) + + assert.Nil(t, err) + assert.NotEmpty(t, streamName) +} + +func TestValidateStreamNameRegexWithDashFlagReturnsNoError(t *testing.T) { + + validStreamName := "my-stream" + + streamName, err := validateStreamName(validStreamName) + + assert.Nil(t, err) + assert.NotEmpty(t, streamName) +} diff --git a/pkg/streamsformatter/streamsFormatter.go b/pkg/streamsformatter/streamsFormatter.go new file mode 100644 index 00000000..d97d3869 --- /dev/null +++ b/pkg/streamsformatter/streamsFormatter.go @@ -0,0 +1,33 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streamsformatter + +import ( + "github.com/galasa-dev/cli/pkg/galasaapi" +) + +//Print in the following fashion: +// name state description +// mystream enabled Experimental tests +// fakestream enabled Fake tests +// +// Total:2 + +// ------------------------------------------------------ +// StreamsFormatter - implemetations can take a collection of stream results +// and turn them into a string for display to the user + +const ( + HEADER_STREAM_NAME = "name" + HEADER_STREAM_STATE = "state" + HEADER_STREAM_DESCRIPTION = "description" +) + +type StreamsFormatter interface { + FormatStreams(streamResults []galasaapi.Stream) (string, error) + GetName() string +} diff --git a/pkg/streamsformatter/summaryFormatter.go b/pkg/streamsformatter/summaryFormatter.go new file mode 100644 index 00000000..cfa533ef --- /dev/null +++ b/pkg/streamsformatter/summaryFormatter.go @@ -0,0 +1,79 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streamsformatter + +import ( + "strconv" + "strings" + + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/utils" +) + +// ----------------------------------------------------- +// Summary format. +const ( + SUMMARY_FORMATTER_NAME = "summary" + ENABLED_STATE = "enabled" + DISABLED_STATE = "disabled" +) + +type StreamsSummaryFormatter struct { +} + +func NewStreamsSummaryFormatter() StreamsFormatter { + return new(StreamsSummaryFormatter) +} + +func (*StreamsSummaryFormatter) GetName() string { + return SUMMARY_FORMATTER_NAME +} + +func (*StreamsSummaryFormatter) FormatStreams(streams []galasaapi.Stream) (string, error) { + + var result string + var err error = nil + buff := strings.Builder{} + totalStreams := len(streams) + + if totalStreams > 0 { + + var table [][]string + var headers = []string{HEADER_STREAM_NAME, HEADER_STREAM_STATE, HEADER_STREAM_DESCRIPTION} + + table = append(table, headers) + + for _, stream := range streams { + + var line []string + var state string + + streamName := stream.Metadata.GetName() + streamDescription := stream.Metadata.GetDescription() + + if stream.GetData().IsEnabled != nil && *stream.GetData().IsEnabled { + state = ENABLED_STATE + } else { + state = DISABLED_STATE + } + + line = append(line, streamName, state, streamDescription) + table = append(table, line) + + } + + columnLengths := utils.CalculateMaxLengthOfEachColumn(table) + utils.WriteFormattedTableToStringBuilder(table, &buff, columnLengths) + + buff.WriteString("\n") + + } + buff.WriteString("Total:" + strconv.Itoa(totalStreams) + "\n") + + result = buff.String() + return result, err +} diff --git a/pkg/streamsformatter/summaryFormatter_test.go b/pkg/streamsformatter/summaryFormatter_test.go new file mode 100644 index 00000000..9e8983db --- /dev/null +++ b/pkg/streamsformatter/summaryFormatter_test.go @@ -0,0 +1,79 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streamsformatter + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/stretchr/testify/assert" +) + +func CreateMockStream(streamName string, isEnabled bool, description string) *galasaapi.Stream { + + var stream = galasaapi.NewStream() + var streamMetadata = galasaapi.NewStreamMetadata() + var streamData = galasaapi.NewStreamData() + + streamMetadata.SetName(streamName) + streamMetadata.SetDescription(description) + streamData.SetIsEnabled(isEnabled) + + stream.SetData(*streamData) + stream.SetMetadata(*streamMetadata) + + return stream + +} + +func TestStreamsSummaryFormatterSingleDataReturnsCorrectly(t *testing.T) { + // Given... + formatter := NewStreamsSummaryFormatter() + + streams := make([]galasaapi.Stream, 0) + stream1 := CreateMockStream("mystream", true, "My test stream") + streams = append(streams, *stream1) + + // When... + actualFormattedOutput, err := formatter.FormatStreams(streams) + + // Then... + assert.Nil(t, err) + expectedFormattedOutput := + `name state description +mystream enabled My test stream + +Total:1 +` + assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +} + +func TestStreamsSummaryFormatterMultipleDataSeperatesWithNewLine(t *testing.T) { + //Given.. + formatter := NewStreamsSummaryFormatter() + + streams := make([]galasaapi.Stream, 0) + stream1 := CreateMockStream("mystream", true, "This is a test stream") + stream2 := CreateMockStream("my-test-stream", false, "Dummy test stream") + stream3 := CreateMockStream("example_stream", true, "Hello stream") + streams = append(streams, *stream1, *stream2, *stream3) + + // When... + actualFormattedOutput, err := formatter.FormatStreams(streams) + + // Then... + assert.Nil(t, err) + expectedFormattedOutput := + `name state description +mystream enabled This is a test stream +my-test-stream disabled Dummy test stream +example_stream enabled Hello stream + +Total:3 +` + assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +} diff --git a/pkg/streamsformatter/yamlFormatter.go b/pkg/streamsformatter/yamlFormatter.go new file mode 100644 index 00000000..2a84c74e --- /dev/null +++ b/pkg/streamsformatter/yamlFormatter.go @@ -0,0 +1,54 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streamsformatter + +import ( + "strings" + + "github.com/galasa-dev/cli/pkg/galasaapi" + "gopkg.in/yaml.v3" +) + +const ( + YAML_FORMATTER_NAME = "yaml" +) + +type StreamsYamlFormatter struct { +} + +func NewStreamsYamlFormatter() StreamsFormatter { + return new(StreamsYamlFormatter) +} + +func (*StreamsYamlFormatter) GetName() string { + return YAML_FORMATTER_NAME +} + +func (*StreamsYamlFormatter) FormatStreams(streams []galasaapi.Stream) (string, error) { + var err error + buff := strings.Builder{} + + for index, stream := range streams { + content := "" + + if index > 0 { + content += "---\n" + } + + var yamlRepresentationBytes []byte + yamlRepresentationBytes, err = yaml.Marshal(stream) + if err == nil { + yamlStr := string(yamlRepresentationBytes) + content += yamlStr + } + + buff.WriteString(content) + } + + result := buff.String() + return result, err +} diff --git a/pkg/streamsformatter/yamlFormatter_test.go b/pkg/streamsformatter/yamlFormatter_test.go new file mode 100644 index 00000000..027582ca --- /dev/null +++ b/pkg/streamsformatter/yamlFormatter_test.go @@ -0,0 +1,141 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package streamsformatter + +import ( + "fmt" + "testing" + + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/stretchr/testify/assert" +) + +const ( + STREAM_DESCRIPTION = "This a Galasa test stream" + MAVEN_REPO_URL = "mvn:myGroup/myArtifact/0.38.0/obr" + TEST_CATALOG_URL = "mvn:myGroup/myArtifact/0.38.0/obr" + MAVEN_GROUP_ID = "myGroup" + MAVEN_ARTIFACT_ID = "myArtifact" + MAVEN_VERSION = "0.38.0" +) + +func createMockTestStreamYamlFormat(streamName string) *galasaapi.Stream { + var stream = galasaapi.NewStream() + + stream.SetKind("GalasaStream") + stream.SetApiVersion("galasa-dev/v1alpha1") + + var streamMetadata = galasaapi.NewStreamMetadata() + streamMetadata.SetDescription(STREAM_DESCRIPTION) + streamMetadata.SetName(streamName) + stream.SetMetadata(*streamMetadata) + + var streamData = galasaapi.NewStreamData() + var testCatalog = galasaapi.NewStreamTestCatalog() + var streamRepoUrl = galasaapi.NewStreamRepository() + + streamRepoUrl.SetUrl(MAVEN_REPO_URL) + testCatalog.SetUrl(TEST_CATALOG_URL) + streamData.SetRepository(*streamRepoUrl) + streamData.SetTestCatalog(*testCatalog) + + // Create OBR data + groupId := MAVEN_GROUP_ID + artifactId := MAVEN_ARTIFACT_ID + version := MAVEN_VERSION + + obrData := galasaapi.StreamOBRData{ + GroupId: &groupId, + ArtifactId: &artifactId, + Version: &version, + } + + obrsSlice := []galasaapi.StreamOBRData{obrData} + streamData.SetObrs(obrsSlice) + + stream.SetData(*streamData) + + return stream +} + +func generateExpectedStreamYaml(streamName string) string { + return fmt.Sprintf( + `apiVersion: galasa-dev/v1alpha1 +kind: GalasaStream +metadata: + name: %s + description: %s +data: + repository: + url: %s + obrs: + - group-id: %s + artifact-id: %s + version: %s + testCatalog: + url: %s`, + streamName, STREAM_DESCRIPTION, MAVEN_REPO_URL, MAVEN_GROUP_ID, MAVEN_ARTIFACT_ID, MAVEN_VERSION, TEST_CATALOG_URL) +} + +func TestSecretsYamlFormatterNoDataReturnsBlankString(t *testing.T) { + // Given... + formatter := NewStreamsYamlFormatter() + formattableStreams := make([]galasaapi.Stream, 0) + + // When... + actualFormattedOutput, err := formatter.FormatStreams(formattableStreams) + + // Then... + assert.Nil(t, err) + expectedFormattedOutput := "" + assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +} + +func TestStreamsYamlFormatterSingleDataReturnsCorrectly(t *testing.T) { + // Given.. + formatter := NewStreamsYamlFormatter() + formattableStreams := make([]galasaapi.Stream, 0) + streamName := "mystream" + + stream1 := createMockTestStreamYamlFormat(streamName) + formattableStreams = append(formattableStreams, *stream1) + + // When... + actualFormattedOutput, err := formatter.FormatStreams(formattableStreams) + + // Then... + assert.Nil(t, err) + expectedFormattedOutput := generateExpectedStreamYaml(streamName) + "\n" + assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +} + +func TestStreamsYamlFormatterMultipleDataReturnsCorrectly(t *testing.T) { + // Given.. + formatter := NewStreamsYamlFormatter() + formattableStreams := make([]galasaapi.Stream, 0) + streamName1 := "mystream" + streamName2 := "mystream2" + + stream1 := createMockTestStreamYamlFormat(streamName1) + stream2 := createMockTestStreamYamlFormat(streamName2) + formattableStreams = append(formattableStreams, *stream1, *stream2) + + // When... + actualFormattedOutput, err := formatter.FormatStreams(formattableStreams) + + // Then... + assert.Nil(t, err) + expectedFormatted1Output := generateExpectedStreamYaml(streamName1) + expectedFormatted2Output := generateExpectedStreamYaml(streamName2) + + expectedFormattedOutput := fmt.Sprintf(`%s +--- +%s +`, expectedFormatted1Output, expectedFormatted2Output) + + assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +}