diff --git a/docker-compose.yml b/docker-compose.yml index 3ba8f15e..2619722b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,8 +12,9 @@ services: - DIR=create environment: AWS_REGION: eu-west-1 - AWS_DYNAMODB_ENDPOINT: http://localstack:4566 AWS_ACCESS_KEY_ID: localstack + AWS_DYNAMODB_ENDPOINT: http://localstack:4566 + AWS_EVENTBRIDGE_ENDPOINT: http://localstack:4566 AWS_SECRET_ACCESS_KEY: localstack DDB_TABLE_NAME_DEEDS: deeds DDB_TABLE_NAME_CHANGES: changes @@ -34,8 +35,9 @@ services: - DIR=update environment: AWS_REGION: eu-west-1 - AWS_DYNAMODB_ENDPOINT: http://localstack:4566 AWS_ACCESS_KEY_ID: localstack + AWS_DYNAMODB_ENDPOINT: http://localstack:4566 + AWS_EVENTBRIDGE_ENDPOINT: http://localstack:4566 AWS_SECRET_ACCESS_KEY: localstack DDB_TABLE_NAME_DEEDS: deeds DDB_TABLE_NAME_CHANGES: changes @@ -56,8 +58,8 @@ services: - DIR=get environment: AWS_REGION: eu-west-1 - AWS_DYNAMODB_ENDPOINT: http://localstack:4566 AWS_ACCESS_KEY_ID: localstack + AWS_DYNAMODB_ENDPOINT: http://localstack:4566 AWS_SECRET_ACCESS_KEY: localstack DDB_TABLE_NAME_DEEDS: deeds DDB_TABLE_NAME_CHANGES: changes diff --git a/go.mod b/go.mod index 1ee8cb2c..28654e6f 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,11 @@ go 1.21 require ( github.com/aws/aws-lambda-go v1.46.0 github.com/aws/aws-sdk-go v1.50.20 + github.com/aws/aws-sdk-go-v2 v1.25.1 + github.com/aws/aws-sdk-go-v2/config v1.26.6 github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.2 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.29.0 + github.com/aws/aws-sdk-go-v2/service/eventbridge v1.29.3 github.com/aws/aws-xray-sdk-go v1.8.3 github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-cmp v0.6.0 @@ -17,8 +20,19 @@ require ( require ( github.com/andybalholm/brotli v1.0.6 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1 // indirect github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.19.1 // indirect - github.com/aws/smithy-go v1.20.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect + github.com/aws/smithy-go v1.20.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -26,6 +40,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.50.0 // indirect golang.org/x/net v0.18.0 // indirect diff --git a/go.sum b/go.sum index 3a31410a..e611a42e 100644 --- a/go.sum +++ b/go.sum @@ -4,42 +4,46 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/aws/aws-lambda-go v1.46.0 h1:UWVnvh2h2gecOlFhHQfIPQcD8pL/f7pVCutmFl+oXU8= github.com/aws/aws-lambda-go v1.46.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= -github.com/aws/aws-sdk-go v1.50.9 h1:yX66aKnEtRc/uNV/1EH8CudRT5aLwVwcSwTBphuVPt8= -github.com/aws/aws-sdk-go v1.50.9/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.50.15 h1:wEMnPfEQQFaoIJwuO18zq/vtG4Ft7NxQ3r9xlEi/8zg= -github.com/aws/aws-sdk-go v1.50.15/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.50.19 h1:YSIDKRSkh/TW0RPWoocdLqtC/T5W6IGBVhFs6P7Qcac= -github.com/aws/aws-sdk-go v1.50.19/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go v1.50.20 h1:xfAnSDVf/azIWTVQXQODp89bubvCS85r70O3nuQ4dnE= github.com/aws/aws-sdk-go v1.50.20/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= -github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.16 h1:KZvXflfyoL43jhDe2tDHPeK9C+edHJl2Rb07N7Dq3qY= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.16/go.mod h1:SdkjT6MneWbTztIxA5cZ8QTvD4ASCeM7IhUkIIhvVa0= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.17 h1:jPuObStSZU1cGheSslAbF2nA4c/IgeIQA1X9frB60Oc= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.17/go.mod h1:df3uvEupLM3MkLim3BDkCaRpgAROW7wk41dwNQjw0kA= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.1 h1:KscYT6xFIz3DtGzCc4xh6U9yxBFViwpITwjAYjMXR0k= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.1/go.mod h1:Nd/DYGFB+ZwgNNPFMkhXYmu8LRluVPHTb+uwYUAez38= +github.com/aws/aws-sdk-go-v2 v1.25.1 h1:P7hU6A5qEdmajGwvae/zDkOq+ULLC9tQBTwqqiwFGpI= +github.com/aws/aws-sdk-go-v2 v1.25.1/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo= +github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o= +github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.2 h1:ksCAKvVacJbsCJAUWaCk4ZS254NByOKlB8V4dGVWC9c= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.2/go.mod h1:vtaNpWHO0v6kWfS27bLuU9dklVj1YmdY/uSc4FqhBE0= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.27.0 h1:e/HPLjLas04wKnmCUSSXD44cYdVjT/Dcd9CkmlYNyNU= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.27.0/go.mod h1:N5tqZcYMM0N1PN7UQYJNWuGyO886OfnMhf/3MAbqMcI= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.27.1 h1:plNo3WtooT2fYnhdyuzzsIJ4QWzcF5AT9oFbnrYC5Dw= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.27.1/go.mod h1:N5tqZcYMM0N1PN7UQYJNWuGyO886OfnMhf/3MAbqMcI= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.28.1 h1:lGR0SYD885g8lGWnT5wNYM/OlUd5ky+a4aiYxBTK3h8= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.28.1/go.mod h1:DxfpJjhSt8Aab1PszcEo63xxUo6mzyUX5shTcxo8LSc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1 h1:evvi7FbTAoFxdP/mixmP7LIYzQWAmzBcwNB/es9XPNc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1/go.mod h1:rH61DT6FDdikhPghymripNUCsf+uVF4Cnk4c4DBKH64= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1 h1:RAnaIrbxPtlXNVI/OIlh1sidTQ3e1qM6LRjs7N0bE0I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1/go.mod h1:nbgAGkH5lk0RZRMh6A4K/oG6Xj11eC/1CyDow+DUAFI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1 h1:rtYJd3w6IWCTVS8vmMaiXjW198noh2PBm5CiXyJea9o= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1/go.mod h1:zvXu+CTlib30LUy4LTNFc6HTZ/K6zCae5YIHTdX9wIo= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.29.0 h1:zZP5rgaQYyDw0nNZRsbYqwC4NS/KsmVKGSwm0EzYAzU= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.29.0/go.mod h1:DxfpJjhSt8Aab1PszcEo63xxUo6mzyUX5shTcxo8LSc= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.7 h1:srShyROqxzC7p18Ws8mqM2sqxJO/8L3Kpiqf+NboJLg= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.7/go.mod h1:9efZgg4nJCGRp91MuHhkwd2kvyp7PWLRYYk5WjEQ5ts= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.19.1 h1:Wd1F42HO5ZJ+auc42VjnSvdUtB3apQdoM/SoRmaq7UA= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.19.1/go.mod h1:0FgUg08+1knEoYHo0pa8ogm7D9sjH79lHnRzCNGk/6Q= +github.com/aws/aws-sdk-go-v2/service/eventbridge v1.29.3 h1:m/JoWWQI/4Ka9WqTgv9ZupD2zePqVMW8PLmLwr+fiGg= +github.com/aws/aws-sdk-go-v2/service/eventbridge v1.29.3/go.mod h1:efCw7VuDRT7Jzj75Tu4Wfx6Pm5Yh6JR2SPSoL7FI1CM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 h1:a33HuFlO0KsveiP90IUJh8Xr/cx9US2PqkSroaLc+o8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0/go.mod h1:SxIkWpByiGbhbHYTo9CMTUnx2G4p4ZQMrDPcRRy//1c= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= github.com/aws/aws-xray-sdk-go v1.8.3 h1:S8GdgVncBRhzbNnNUgTPwhEqhwt2alES/9rLASyhxjU= github.com/aws/aws-xray-sdk-go v1.8.3/go.mod h1:tv8uLMOSCABolrIF8YCcp3ghyswArsan8dfLCA1ZATk= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= -github.com/aws/smithy-go v1.20.0 h1:6+kZsCXZwKxZS9RfISnPc4EXlHoyAkm2hPuM8X2BrrQ= -github.com/aws/smithy-go v1.20.0/go.mod h1:uo5RKksAl4PzhqaAbjd4rLgFoq5koTsQKYuGe7dklGc= +github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= +github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -50,8 +54,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -81,6 +83,11 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -107,5 +114,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/event/client.go b/internal/event/client.go new file mode 100644 index 00000000..e3793318 --- /dev/null +++ b/internal/event/client.go @@ -0,0 +1,54 @@ +package event + +import ( + "context" + "encoding/json" + "os" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eventbridge" + "github.com/aws/aws-sdk-go-v2/service/eventbridge/types" +) + +const source = "opg.poas.lpastore" + +type EventBridgeClient interface { + PutEvents(ctx context.Context, params *eventbridge.PutEventsInput, optFns ...func(*eventbridge.Options)) (*eventbridge.PutEventsOutput, error) +} + +type Client struct { + eventBusName string + svc EventBridgeClient +} + +func NewClient(cfg aws.Config, eventBusName string) *Client { + return &Client{ + svc: eventbridge.NewFromConfig(cfg, func (o *eventbridge.Options) { + o.BaseEndpoint = aws.String(os.Getenv("AWS_EVENTBRIDGE_ENDPOINT")) + }), + eventBusName: eventBusName, + } +} + +func (c *Client) SendLpaUpdated(ctx context.Context, event LpaUpdated) error { + return c.send(ctx, "lpa-updated", event) +} + +func (c *Client) send(ctx context.Context, eventType string, detail any) error { + + v, err := json.Marshal(detail) + if err != nil { + return err + } + + _, err = c.svc.PutEvents(ctx, &eventbridge.PutEventsInput{ + Entries: []types.PutEventsRequestEntry{{ + EventBusName: aws.String(c.eventBusName), + Source: aws.String(source), + DetailType: aws.String(eventType), + Detail: aws.String(string(v)), + }}, + }) + + return err +} diff --git a/internal/event/client_test.go b/internal/event/client_test.go new file mode 100644 index 00000000..ffee2f20 --- /dev/null +++ b/internal/event/client_test.go @@ -0,0 +1,49 @@ +package event + +import ( + "context" + "encoding/json" + "errors" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eventbridge" + "github.com/aws/aws-sdk-go-v2/service/eventbridge/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type mockEventBridgeClient struct { + mock.Mock +} + +func (_m *mockEventBridgeClient) PutEvents(ctx context.Context, params *eventbridge.PutEventsInput, optFns ...func(*eventbridge.Options)) (*eventbridge.PutEventsOutput, error) { + var r0 *eventbridge.PutEventsOutput + var r1 error = errors.New("err") + + return r0, r1 +} + +func TestClientSendEvent(t *testing.T) { + ctx := context.Background() + expectedError := errors.New("err") + + event := LpaUpdated{ Uid: "M-1234-1234-1234", ChangeType: "CREATED" } + data, _ := json.Marshal(event) + + mockClient := &mockEventBridgeClient{} + mockClient.On("PutEvents", mock.Anything, &eventbridge.PutEventsInput{ + Entries: []types.PutEventsRequestEntry{{ + EventBusName: aws.String("my-bus"), + Source: aws.String("opg.poas.lpastore"), + DetailType: aws.String("lpa-updated"), + Detail: aws.String(string(data)), + }}, + }). + Return(nil, expectedError) + + svc := &Client{svc: mockClient, eventBusName: "my-bus"} + err := svc.SendLpaUpdated(ctx, event) + + assert.Equal(t, expectedError, err) +} diff --git a/internal/event/events.go b/internal/event/events.go new file mode 100644 index 00000000..ba7c11b9 --- /dev/null +++ b/internal/event/events.go @@ -0,0 +1,6 @@ +package event + +type LpaUpdated struct { + Uid string `json:"uid"` + ChangeType string `json:"changeType"` +} diff --git a/lambda/create/main.go b/lambda/create/main.go index 607635a3..fc9d49a2 100644 --- a/lambda/create/main.go +++ b/lambda/create/main.go @@ -8,11 +8,17 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-sdk-go-v2/config" "github.com/ministryofjustice/opg-data-lpa-store/internal/ddb" + "github.com/ministryofjustice/opg-data-lpa-store/internal/event" "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" "github.com/ministryofjustice/opg-go-common/logging" ) +type EventClient interface { + SendLpaUpdated(ctx context.Context, event event.LpaUpdated) error +} + type Logger interface { Print(...interface{}) } @@ -27,13 +33,14 @@ type Verifier interface { } type Lambda struct { - store Store - verifier Verifier - logger Logger + eventClient EventClient + store Store + verifier Verifier + logger Logger } -func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - _, err := l.verifier.VerifyHeader(event) +func (l *Lambda) HandleEvent(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + _, err := l.verifier.VerifyHeader(req) if err != nil { l.logger.Print("Unable to verify JWT from header") return shared.ProblemUnauthorisedRequest.Respond() @@ -42,7 +49,7 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe l.logger.Print("Successfully parsed JWT from event header") var input shared.LpaInit - uid := event.PathParameters["uid"] + uid := req.PathParameters["uid"] response := events.APIGatewayProxyResponse{ StatusCode: 500, @@ -63,7 +70,7 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe return problem.Respond() } - err = json.Unmarshal([]byte(event.Body), &input) + err = json.Unmarshal([]byte(req.Body), &input) if err != nil { l.logger.Print(err) return shared.ProblemInternalServerError.Respond() @@ -91,6 +98,16 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe return shared.ProblemInternalServerError.Respond() } + // send lpa-updated event + err = l.eventClient.SendLpaUpdated(ctx, event.LpaUpdated{ + Uid: uid, + ChangeType: "CREATED", + }) + + if err != nil { + l.logger.Print(err) + } + // respond response.StatusCode = 201 response.Body = `{}` @@ -99,14 +116,22 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe } func main() { + logger := logging.New(os.Stdout, "opg-data-lpa-store") + ctx := context.Background() + awsConfig, err := config.LoadDefaultConfig(ctx) + if err != nil { + logger.Print("Failed to load configuration:", err) + } + l := &Lambda{ + eventClient: event.NewClient(awsConfig, os.Getenv("EVENT_BUS_NAME")), store: ddb.New( os.Getenv("AWS_DYNAMODB_ENDPOINT"), os.Getenv("DDB_TABLE_NAME_DEEDS"), os.Getenv("DDB_TABLE_NAME_CHANGES"), ), verifier: shared.NewJWTVerifier(), - logger: logging.New(os.Stdout, "opg-data-lpa-store"), + logger: logger, } lambda.Start(l.HandleEvent) diff --git a/lambda/update/main.go b/lambda/update/main.go index 814c1817..b7d161d4 100644 --- a/lambda/update/main.go +++ b/lambda/update/main.go @@ -8,12 +8,18 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-sdk-go-v2/config" "github.com/google/uuid" "github.com/ministryofjustice/opg-data-lpa-store/internal/ddb" + "github.com/ministryofjustice/opg-data-lpa-store/internal/event" "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" "github.com/ministryofjustice/opg-go-common/logging" ) +type EventClient interface { + SendLpaUpdated(ctx context.Context, event event.LpaUpdated) error +} + type Logger interface { Print(...interface{}) } @@ -28,13 +34,14 @@ type Verifier interface { } type Lambda struct { - store Store - verifier Verifier - logger Logger + eventClient EventClient + store Store + verifier Verifier + logger Logger } -func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - claims, err := l.verifier.VerifyHeader(event) +func (l *Lambda) HandleEvent(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + claims, err := l.verifier.VerifyHeader(req) if err != nil { l.logger.Print("Unable to verify JWT from header") return shared.ProblemUnauthorisedRequest.Respond() @@ -48,12 +55,12 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe } var update shared.Update - if err = json.Unmarshal([]byte(event.Body), &update); err != nil { + if err = json.Unmarshal([]byte(req.Body), &update); err != nil { l.logger.Print(err) return shared.ProblemInternalServerError.Respond() } - lpa, err := l.store.Get(ctx, event.PathParameters["uid"]) + lpa, err := l.store.Get(ctx, req.PathParameters["uid"]) if err != nil { l.logger.Print(err) return shared.ProblemInternalServerError.Respond() @@ -94,6 +101,16 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe return shared.ProblemInternalServerError.Respond() } + // send lpa-updated event + err = l.eventClient.SendLpaUpdated(ctx, event.LpaUpdated{ + Uid: lpa.Uid, + ChangeType: "UPDATED", + }) + + if err != nil { + l.logger.Print(err) + } + response.StatusCode = 201 response.Body = string(body) @@ -101,14 +118,22 @@ func (l *Lambda) HandleEvent(ctx context.Context, event events.APIGatewayProxyRe } func main() { + logger := logging.New(os.Stdout, "opg-data-lpa-store") + ctx := context.Background() + awsConfig, err := config.LoadDefaultConfig(ctx) + if err != nil { + logger.Print("Failed to load configuration:", err) + } + l := &Lambda{ + eventClient: event.NewClient(awsConfig, os.Getenv("EVENT_BUS_NAME")), store: ddb.New( os.Getenv("AWS_DYNAMODB_ENDPOINT"), os.Getenv("DDB_TABLE_NAME_DEEDS"), os.Getenv("DDB_TABLE_NAME_CHANGES"), ), verifier: shared.NewJWTVerifier(), - logger: logging.New(os.Stdout, "opg-data-lpa-store"), + logger: logger, } lambda.Start(l.HandleEvent) diff --git a/lambda/update/main_test.go b/lambda/update/main_test.go index dacb9259..75bdfc51 100644 --- a/lambda/update/main_test.go +++ b/lambda/update/main_test.go @@ -14,13 +14,32 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/golang-jwt/jwt/v5" + "github.com/ministryofjustice/opg-data-lpa-store/internal/event" "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" "github.com/ministryofjustice/opg-go-common/logging" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) var expectedError = errors.New("expected") +type mockLogger struct { + mock.Mock +} + +func (m *mockLogger) Print(v ...interface{}) { + m.Called(v...) +} + +type mockEventClient struct { + mock.Mock +} + +func (m *mockEventClient) SendLpaUpdated(ctx context.Context, event event.LpaUpdated) error { + args := m.Called(ctx, event) + return args.Error(0) +} + type mockStore struct { get shared.Lpa getErr error @@ -51,9 +70,12 @@ func (m *mockVerifier) VerifyHeader(events.APIGatewayProxyRequest) (*shared.LpaS func TestHandleEvent(t *testing.T) { store := &mockStore{get: shared.Lpa{Uid: "1"}} + client := mockEventClient{} + client.On("SendLpaUpdated", mock.Anything, mock.Anything).Return(nil) l := Lambda{ - store: store, - verifier: &mockVerifier{ + eventClient: &client, + store: store, + verifier: &mockVerifier{ claims: shared.LpaStoreClaims{ RegisteredClaims: jwt.RegisteredClaims{ Subject: "1234", @@ -105,13 +127,14 @@ func TestHandleEvent(t *testing.T) { store.update, cmpopts.IgnoreFields(shared.Update{}, "Id", "Applied"), )) + client.AssertExpectations(t) } func TestHandleEventWhenUnknownType(t *testing.T) { l := Lambda{ - store: &mockStore{get: shared.Lpa{Uid: "1"}}, - verifier: &mockVerifier{}, - logger: logging.New(io.Discard, ""), + store: &mockStore{get: shared.Lpa{Uid: "1"}}, + verifier: &mockVerifier{}, + logger: logging.New(io.Discard, ""), } resp, err := l.HandleEvent(context.Background(), events.APIGatewayProxyRequest{ @@ -124,9 +147,9 @@ func TestHandleEventWhenUnknownType(t *testing.T) { func TestHandleEventWhenUpdateInvalid(t *testing.T) { l := Lambda{ - store: &mockStore{get: shared.Lpa{Uid: "1"}}, - verifier: &mockVerifier{}, - logger: logging.New(io.Discard, ""), + store: &mockStore{get: shared.Lpa{Uid: "1"}}, + verifier: &mockVerifier{}, + logger: logging.New(io.Discard, ""), } resp, err := l.HandleEvent(context.Background(), events.APIGatewayProxyRequest{ @@ -190,3 +213,36 @@ func TestHandleEventWhenHeaderNotVerified(t *testing.T) { assert.Equal(t, 401, resp.StatusCode) assert.JSONEq(t, `{"code":"UNAUTHORISED","detail":"Invalid JWT"}`, resp.Body) } + +func TestHandleEventWhenSendLpaUpdatedFailed(t *testing.T) { + store := &mockStore{get: shared.Lpa{Uid: "1"}} + client := mockEventClient{} + client.On("SendLpaUpdated", mock.Anything, mock.Anything).Return(errors.New("Update failed")) + + logger := mockLogger{} + logger.On("Print", "Successfully parsed JWT from event header") + logger.On("Print", errors.New("Update failed")) + + l := Lambda{ + eventClient: &client, + store: store, + verifier: &mockVerifier{ + claims: shared.LpaStoreClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + Subject: "1234", + }, + }, + }, + logger: &logger, + } + + resp, err := l.HandleEvent(context.Background(), events.APIGatewayProxyRequest{ + Body: `{"type":"CERTIFICATE_PROVIDER_SIGN","changes":[{"key":"/certificateProvider/signedAt","old":null,"new":"2022-01-02T12:13:14.000000006Z"},{"key":"/certificateProvider/contactLanguagePreference","old":null,"new":"en"}]}`, + }) + + client.AssertExpectations(t) + logger.AssertExpectations(t) + + assert.Nil(t, err) + assert.Equal(t, 201, resp.StatusCode) +}