Skip to content

Commit

Permalink
Uml 2978 Add System Message feature to Admin portal (#2452)
Browse files Browse the repository at this point in the history
* fix keys

* created system message page to admin portal

* refactoring system message page

* gohtml template layout

* started connecting up to parameter store code (WIP)

* test for server.go is passing now

* WIP - getting tests to work

* test now working

* write method and test WIP not yet working

* test for system message, uses prefix in ssm connection

* mocked out service in tests (WIP)

* WIP textareas are populated by hard-coded strings -  next step they will
come from the paramter store via the service

* call service to get textarea values

* fix golint errors

* golint fix

* WIP now saves to localhost parameter store but handler needs fixing

* WIP fixes

* refactored template and amended handler

* added overwrite to ssm PutSystemMessage

* added test for PutParameter functionality

* refactor of handler with bilingual message validation check

* Added delete message & success/error messages

* service message removed message

* tests for data/system_message

* tests for systemmessage handler

* reformatting for linting etc

* linting fix

* later onelogin mock

* use SSM URL

* test coverage for system message handler parse form error

* refactoring system message handler test.

* log endpoint on startup, to debug issue in aws

* log to stdout

* change env var

* Try with no baseendpoint

* fix linter error

* fix linter errors

* fix linter

* Fix SSM endpoint

* amended to systemmessage handler test to include no validation error

* added pointer

* lint

* extra test coverage - data side

* env var

* Add environment name

* use environment name as prefix when accessing paramter store

* fixes

* use /system-message/ENV/en/cy for for paramter store

* fix

* fix prefix logic

* keep linter happy / improve comment

---------

Co-authored-by: Lowenna Baggaley <[email protected]>
Co-authored-by: Sam Ainsworth <[email protected]>
  • Loading branch information
3 people authored Feb 7, 2024
1 parent 02f608a commit 7763413
Show file tree
Hide file tree
Showing 19 changed files with 950 additions and 153 deletions.
2 changes: 1 addition & 1 deletion docker-compose.dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ services:

mock-one-login:
container_name: mock-one-login
image: 311462405659.dkr.ecr.eu-west-1.amazonaws.com/use_an_lpa/mock_onelogin_app:v0.58.0
image: 311462405659.dkr.ecr.eu-west-1.amazonaws.com/use_an_lpa/mock_onelogin_app:v0.60.0
ports:
- "4013:8080"
environment:
Expand Down
2 changes: 1 addition & 1 deletion service-admin/.idea/runConfigurations/Admin_Service.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 22 additions & 3 deletions service-admin/cmd/admin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"os/signal"
"time"

"github.com/aws/aws-sdk-go-v2/service/ssm"

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/config"
Expand All @@ -19,7 +21,6 @@ import (
"github.com/ministryofjustice/opg-use-an-lpa/service-admin/internal/server/data"
"github.com/ministryofjustice/opg-use-an-lpa/service-admin/internal/server/handlers"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
)

Expand All @@ -40,6 +41,11 @@ func main() {
env.Get("AWS_REGION", "eu-west-1"),
"",
)
ssmEndpoint = flag.String(
"ssmEndpoint",
env.Get("AWS_ENDPOINT_SSM", ""),
"",
)
dbTablePrefix = flag.String(
"dbTablePrefix",
env.Get("ADMIN_DYNAMODB_TABLE_PREFIX", ""),
Expand All @@ -63,8 +69,14 @@ func main() {
lpaCodesEndpoint = flag.String(
"lpa-codes-endpoint",
env.Get("LPA_CODES_API_ENDPOINT", ""),
"The codes enpoint",
"The codes endpoint",
)
environmentName = flag.String(
"environment-name",
env.Get("ENVIRONMENT_NAME", ""),
"Environment name - used to avoid clashes between CI environments",
)
log = zerolog.New(os.Stdout).With().Timestamp().Logger()
)

flag.Parse()
Expand All @@ -84,11 +96,18 @@ func main() {

dynamoDB := data.NewDynamoConnection(config, *dbEndpoint, *dbTablePrefix)

ssmConn := data.NewSSMConnection(ssm.NewFromConfig(config, func(o *ssm.Options) {
if *ssmEndpoint != "" {
endpoint := *ssmEndpoint
o.BaseEndpoint = &endpoint
}
}), *environmentName)

activationKeyService := createActivationKeyService(*lpaCodesEndpoint, *dynamoDB, config)

zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack

app := server.NewAdminApp(*dynamoDB, mux.NewRouter(), handlers.NewTemplateWriterService(), activationKeyService)
app := server.NewAdminApp(*dynamoDB, *ssmConn, mux.NewRouter(), handlers.NewTemplateWriterService(), activationKeyService)

srv := &http.Server{
Handler: app.InitialiseServer(*keyURL, u),
Expand Down
50 changes: 0 additions & 50 deletions service-admin/internal/server/data/parameter_store.go

This file was deleted.

74 changes: 0 additions & 74 deletions service-admin/internal/server/data/parameter_store_test.go

This file was deleted.

40 changes: 40 additions & 0 deletions service-admin/internal/server/data/ssm_connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package data

import (
"context"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"strings"
)

type SSMClient interface {
PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error)
GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
DeleteParameter(ctx context.Context, params *ssm.DeleteParameterInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParameterOutput, error)
}

type SSMConnection struct {
Prefix string
Client SSMClient
}

func NewSSMConnection(client SSMClient, prefix string) *SSMConnection {
return &SSMConnection{Client: client, Prefix: prefix}
}

func (s *SSMConnection) prefixedParameterName(name string) string {
// if no prefix, then return unchanged
if s.Prefix == "" {
return name
}

// if or no / to insert prefix next to, then return unchanged
if !strings.Contains(name[1:], "/") {
return name
}

i := strings.Index(name[1:], "/")

x := name[:i+1] + "/" + s.Prefix + "/" + strings.TrimLeft(name[i+1:], "/")

return x
}
31 changes: 31 additions & 0 deletions service-admin/internal/server/data/ssm_connection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package data_test

import (
"context"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/ministryofjustice/opg-use-an-lpa/service-admin/internal/server/data"
"github.com/stretchr/testify/assert"
"testing"
)

type MockSSMClient struct{}

func (m MockSSMClient) PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) {
return nil, nil
}

func (m MockSSMClient) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
return nil, nil
}

func (m MockSSMClient) DeleteParameter(ctx context.Context, params *ssm.DeleteParameterInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParameterOutput, error) {
return nil, nil
}

// test for new method, which takes in ssmclient
func TestNewSSMConnection(t *testing.T) {
t.Parallel()

got := data.NewSSMConnection(&MockSSMClient{}, "")
assert.NotNil(t, got)
}
75 changes: 75 additions & 0 deletions service-admin/internal/server/data/system_message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package data

import (
"context"
"errors"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/ssm/types"
"github.com/rs/zerolog/log"
)

type SystemMessageService struct {
ssmConn SSMConnection
}

func NewSystemMessageService(ssmConn SSMConnection) *SystemMessageService {
return &SystemMessageService{ssmConn: ssmConn}
}

func (s *SystemMessageService) GetSystemMessages(ctx context.Context) (map[string]string, error) {
messageKeys := []string{"/system-message/use/en", "/system-message/use/cy", "/system-message/view/en", "/system-message/view/cy"}
messages := make(map[string]string)

for _, messageKey := range messageKeys {
messageText, err := s.ssmConn.Client.GetParameter(ctx, &ssm.GetParameterInput{
Name: aws.String(s.ssmConn.prefixedParameterName(messageKey)),
WithDecryption: aws.Bool(true),
})
if err != nil {
log.Error().Err(err).Msg(fmt.Sprintf("error retrieving parameter: %s", messageKey))
continue
}

if messageText != nil && messageText.Parameter != nil && messageText.Parameter.Value != nil {
messages[messageKey] = *messageText.Parameter.Value
}
}

return messages, nil
}

func (s *SystemMessageService) PutSystemMessages(ctx context.Context, messages map[string]string) (bool, error) {
deleted := false

for messageKey, messageValue := range messages {
if messageValue != "" {
_, err := s.ssmConn.Client.PutParameter(ctx, &ssm.PutParameterInput{
Name: aws.String(s.ssmConn.prefixedParameterName(messageKey)),
Value: aws.String(messageValue),
Type: types.ParameterTypeString,
Overwrite: aws.Bool(true),
})
if err != nil {
return false, fmt.Errorf("error writing parameter: %w", err)
}
} else {
_, err := s.ssmConn.Client.DeleteParameter(ctx, &ssm.DeleteParameterInput{
Name: aws.String(s.ssmConn.prefixedParameterName(messageKey)),
})
if err != nil {
var pnf *types.ParameterNotFound
if errors.As(err, &pnf) {
log.Debug().Msg(fmt.Sprintf("not deleting parameter '%s' as it does not exist", messageKey))
} else {
return false, fmt.Errorf("error deleting parameter: %w", err)
}
} else {
deleted = true
}
}
}

return deleted, nil
}
Loading

0 comments on commit 7763413

Please sign in to comment.