-
Notifications
You must be signed in to change notification settings - Fork 6
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
use official ZIO-lambda library in product-move-api #2115
Conversation
@@ -568,6 +568,7 @@ lazy val `product-move-api` = lambdaProject( | |||
"com.softwaremill.sttp.tapir" %% "tapir-core" % tapirVersion, | |||
"com.softwaremill.sttp.tapir" %% "tapir-json-zio" % tapirVersion, | |||
"com.softwaremill.sttp.tapir" %% "tapir-aws-lambda" % tapirVersion, | |||
"com.softwaremill.sttp.tapir" %% "tapir-aws-lambda-zio" % tapirVersion, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the new library added in softwaremill/tapir#2975
val layer: Layer[Throwable, AwsCredentialsProvider] = | ||
ZLayer.scoped(ZIO.fromAutoCloseable(ZIO.attempt(impl))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the official zio interpreter expects Throwable as the error, rather than Any, so I had to make everything comply. That is the majority of the changes.
@main | ||
// this will output the HTML to the console | ||
def testDocsHtml(): Unit = { | ||
Handler.runTest( | ||
"GET", | ||
"/docs/", | ||
None, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new manual test so I could reproduce the issue locally.
ZLayer.scoped(for { | ||
stage <- ZIO.service[Stage] | ||
sqsClient <- initializeSQSClient().mapError(ex => | ||
InternalServerError(s"Failed to initialize SQS Client with error: $ex"), | ||
new Throwable(s"Failed to initialize SQS Client with error", ex), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we were returning InternalServerError but I don't know if it was actually getting used directly, or just converted into an internal server error later anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check? We obvs want to maintain the existing behaviour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that's a really good point I will check that for sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, existing code we end up with Any
as the failure type in the ZIO, and that ends up here in the code
Line 126 in 1b7e0ee
AwsResponse(false, 500, Map.empty, "") |
So no matter whether we called it InternalServerError or BadRequest etc, it it would still be an internal server error.
If/where we map the error to a ZIO.success then we could use it as a response.
I think the new (official) way of using Throwable rather than my way of using Any as the error type is better as it's pretty clear that a Throwable will be a 500 error, and if we try to use a BadRequest or something, expecting it will really be a bad request, it won't compile unless we recover it into a success.
object Handler | ||
extends ZIOApiGatewayRequestHandler( | ||
List( | ||
AvailableProductMovesEndpoint.server, | ||
ProductMoveEndpoint.server, | ||
UpdateSupporterPlusAmountEndpoint.server, | ||
SubscriptionCancelEndpoint.server, | ||
), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed all the test stuff from the handler and made the endpoints a constructor parameter.
This was because with inheritance and vals I was getting issues with initialisation order, and server
was coming out as null.
Rather than just going down the lazy val option (which worked) I decided to separate/simplify appropriately.
"POST", | ||
"/product-move/A-S123", | ||
Some(ProductMoveEndpointTypes.ExpectedInput(49.99, false, None, None).toJson), | ||
"/product-move/recurring-contribution-to-supporter-plus/" + devSubscriptionNumber, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was not matching the actual routes definiton.
) | ||
} | ||
|
||
// for testing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this functions moved out of the superclass ZIOApiGatewayRequestHandler
|
||
val credentialsProvider = AwsCredentialsProviderChain | ||
private lazy val secretsClient = SecretsManagerClient.builder().credentialsProvider(credentialsProvider).build() | ||
lazy val credentialsProvider = AwsCredentialsProviderChain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in another PR this should probably use the credentials provider layer like the other AWS layers
str = new String(allBytes, StandardCharsets.UTF_8) | ||
_ <- ZIO.log("input: " + str) | ||
loggedOutputStream = new ByteArrayOutputStream() | ||
_ <- handler.process[AwsRequestV1](new ByteArrayInputStream(allBytes), loggedOutputStream) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to use V1 because we are still using the 1.0 integration
https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
I'm not sure if we should be updating or not, it doesn't seem to encourage it in the documentation.
.provideLayer(Runtime.removeDefaultLoggers) | ||
.provideLayer(Runtime.addLogger(new AwsLambdaLogger(context.getLogger))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the lambda logger support, this means we use the default logging.
I'm not sure this was actually still working anyway, as looking in PROD I can't see the timestamps being generated.
Ultimately everything that goes to stdout will end up in the cloudwatch logs, it might just not look nice.
|
||
object ZIOApiGatewayRequestHandler { | ||
abstract class ZIOApiGatewayRequestHandler(val server: List[ServerEndpoint[Any, Task]]) extends RequestStreamHandler { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this class has basically been rewritten according to the example
https://github.com/softwaremill/tapir/blob/master/serverless/aws/lambda-zio-tests/src/main/scala/sttp/tapir/serverless/aws/ziolambda/tests/ZioLambdaHandlerImpl.scala
with the addition of request and response stream logging
@@ -65,20 +62,11 @@ object Secrets { | |||
def getSalesforceSSLSecrets: RIO[Secrets, SalesforceSSLSecrets] = | |||
ZIO.environmentWithZIO[Secrets](_.get.getSalesforceSSLSecrets) | |||
} | |||
object SecretsLive extends Secrets { | |||
class SecretsLive(secretsClient: SecretsManagerClient) extends Secrets { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is updated to match how the S3 client works, the credentials come from the AwsCredentialsLive.layer
ZLayer.scoped { | ||
for { | ||
creds <- ZIO.service[AwsCredentialsProvider] | ||
s3Client <- ZIO.fromAutoCloseable(ZIO.attempt(impl(creds))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an important change - the SecretsManagerClient needs to be .close()
d after use
@@ -84,7 +85,7 @@ object InvoiceItemAdjustmentSpec extends ZIOSpecDefault { | |||
), | |||
) | |||
assert(adjustments.length)(equalTo(2)) && | |||
assert(adjustments.head.AdjustmentDate.getDayOfMonth)(equalTo(15)) | |||
assert(adjustments.head.AdjustmentDate.getDayOfMonth)(equalTo(15)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't agree with this, but scalafmt complains otherwise
[warn] scalafmt: ~/code/support-service-lambdas/handlers/product-move-api/src/test/scala/com/gu/productmove/zuora/InvoiceItemAdjustmentSpec.scala isn't formatted properly!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one question about error types
run(subscriptionName).tapEither(result => ZIO.log("result tapped: " + result)).map(Right.apply) | ||
} | ||
} | ||
|
||
// sub to test on: "A-S00334930" | ||
private def run(subscriptionName: String): TIO[OutputBody] = | ||
runWithEnvironment(SubscriptionName("A-S00334930")).provide( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this shouldn't have been hard coded, it should have used the one passed from the client!
I noticed that the docs endpoint for product-move-api was returning a 500 error
When I looked into it, it was because I hadn't covered all the cases in the custom ZIO/aws lambda interpreter I wrote for tapir early last year.
Rather than adding the one case, I realised that a few months ago they added an official interpreter if we bumped to a later version of tapir. softwaremill/tapir#2975
So I switched over to the new one, which worked fine. I also had to change to the more standard ZIO Task rather than using our own custom TIO, which meant changing the error type from Any to Throwable. This might help our error stack traces, which can often be a bit unclear with other types.
Tested in CODE and working again
Feel free to take a look yourself at the docs https://product-move-api-code.support.guardianapis.com/docs/
Also tested the available moves endpoint
{{product-movement-baseurl}}/available-product-moves/A-S00737090
response data looks ok