Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Netlify http4s handler #89

Closed
wants to merge 13 commits into from
Closed

Conversation

kubukoz
Copy link
Member

@kubukoz kubukoz commented Dec 14, 2021

Might close #74 in the future.

image

Instructions for testing:

  1. get netlify-cli (e.g. nix shell nixpkgs#netlify-cli)
  2. run sbt fastOptJS
  3. cp netlify/.js/target/scala-2.13/netlify-fastopt/main.js netlify/functions/example.js (netlify/functions is the default directory for functions)
  4. run netlify functions:serve --debug
  5. send curls to localhost:9999/.netlify/functions/example

build.sbt Outdated Show resolved Hide resolved
build.sbt Outdated Show resolved Hide resolved
for {
event <- env.event
method <- Method.fromString(event.httpMethod).liftTo[F]
uri <- Uri.fromString(event.path).liftTo[F]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this is the relative path. Is that correct or should it be the full URL?

netlify/src/main/scala/feral/netlify/handler.scala Outdated Show resolved Hide resolved
@kubukoz kubukoz marked this pull request as ready for review December 15, 2021 15:36
Copy link
Member

@armanbilge armanbilge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, the only kind of netlify function possible is an http handler? In which case, I think we can bake this in much more. E.g.

  • No need for Event and Result types or Option for the response or implicit decoders/encoders. We can prescribe exactly what these are going to be.
  • We could even have IOFunction.Simple be implemented directly with an HttpRoutes.

build.sbt Outdated Show resolved Hide resolved
event <- env.event
method <- Method.fromString(event.httpMethod).liftTo[F]
uri <- Uri.fromString(event.path).liftTo[F]
headers = Headers(event.headers.toList)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this problem also applies to the AWS lambda which is admittedly half-baked, do we need to include multiValueHeaders here as well? Also the rawQuery.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably so, yeah. I'll check and add them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still not done, todo

@kubukoz
Copy link
Member Author

kubukoz commented Dec 15, 2021

IIUC, the only kind of netlify function possible is an http handler? In which case, I think we can bake this in much more.

There's more - you can have functions triggered by deployment events etc. https://docs.netlify.com/functions/trigger-on-events/

technically, it seems like these have the same interface but they have a common body schema, except there's a "background function" type that might not have to return...

@armanbilge
Copy link
Member

Huh, interesting. As you point out it seems that the http Event/Response are the common shared interface (idk about the background one, but what's the harm in returning a 204 or something lol). Then (a future PR can) add models for those additional triggers and maybe some convenience builders based on them.

@armanbilge armanbilge marked this pull request as draft December 21, 2021 18:40
@@ -31,7 +31,7 @@ private[lambda] trait IOLambdaPlatform[Event, Result] {
js.Dynamic.global.exports.updateDynamic(handlerName)(handlerFn)
}

private lazy val handlerFn
protected lazy val handlerFn
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: fix this after #116 lands

import cats.effect.IO
import feral.lambda.Context

object example extends IOFunction.Simple {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be removed later

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth moving to examples?

@@ -17,14 +17,17 @@
package feral.lambda.facade

import scala.scalajs.js
import scala.scalajs.js._
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick :P

Suggested change
import scala.scalajs.js._
import scala.scalajs.js.|

context.memoryLimitInMB.toInt,
(context.memoryLimitInMB: Any) match {
case s: String => s.toInt
case i: Int => i
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why isn't fatal warnings complaining about this 😆

import io.circe.Decoder
import io.circe.Encoder

final case class HttpFunctionEvent(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh also let's add a KernelSource for this so the tracing middleware works too.


object IOFunction {

abstract class Simple extends IOFunction {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this extend IOLambda.Simple instead and avoid the repetition?

import cats.effect.IO
import feral.lambda.Context

object example extends IOFunction.Simple {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth moving to examples?

Comment on lines +28 to +29
// Note: This should always be a string, but the Netlify CLI currently passes a number.
// ref: https://github.com/netlify/functions/pull/251
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstreamed again to ashiina/lambda-local#218 but I'm not optimistic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and released!

@armanbilge
Copy link
Member

armanbilge commented Dec 22, 2021

So apparently a netlify Context looks like this:

{
  callbackWaitsForEmptyEventLoop: [Getter/Setter],
  succeed: [Function (anonymous)],
  fail: [Function (anonymous)],
  done: [Function (anonymous)],
  functionVersion: '$LATEST',
  functionName: '164d0769d4ca9c18ce26562dca3f9e408070c2d1c2b884621e91527e1724e77d',
  memoryLimitInMB: '1024',
  logGroupName: '/aws/lambda/164d0769d4ca9c18ce26562dca3f9e408070c2d1c2b884621e91527e1724e77d',
  logStreamName: '2021/08/03/[$LATEST]d3dabe643c01435bb985e7b0ae5821d0',
  clientContext: {
    custom: {
      netlify: 'eyJpZGVudGl0eSI6eyJ1cmwiOiJodHRwczovL2Fkb3JpbmctYWxiYXR0YW5pLWEyODkwNi5uZXRsaWZ5LmFwcC8ubmV0bGlmeS9pZGVudGl0eSIsInRva2VuIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmxlSEFpT2pFMk1qYzVOekkxTVRRc0luTjFZaUk2SWpBaWZRLlJGSDVwRDlPUGZUa2lSb3RscXdRekJmbU1JWlF0QWFOVGNkYlVnX2N6WWMifSwidXNlciI6eyJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCJ9LCJlbWFpbCI6InRlc3RAdGVzdC5jb20iLCJleHAiOjE2Mjc5NzU5NjgsInN1YiI6Ijc3NWE1MzQ0LTBjNmQtNGRjNi05YWM3LWQyZDY0NmUwNzFlYyIsInVzZXJfbWV0YWRhdGEiOnsiZnVsbF9uYW1lIjoiVGVzdCBVc2VyIn19LCJzaXRlX3VybCI6Imh0dHBzOi8vYWRvcmluZy1hbGJhdHRhbmktYTI4OTA2Lm5ldGxpZnkuYXBwIn0='
    },
    identity: {
      url: 'https://adoring-albattani-a28906.netlify.app/.netlify/identity',
      token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Mjc5NzI1MTQsInN1YiI6IjAifQ.RFH5pD9OPfTkiRotlqwQzBfmMIZQtAaNTcdbUg_czYc'
    },
    user: {
      app_metadata: [Object],
      email: '[email protected]',
      exp: 1627975968,
      sub: '775a5344-0c6d-4dc6-9ac7-d2d646e071ec',
      user_metadata: [Object]
    }
  },
  identity: undefined,
  invokedFunctionArn: 'arn:aws:lambda:us-east-1:375708248955:function:164d0769d4ca9c18ce26562dca3f9e408070c2d1c2b884621e91527e1724e77d',
  awsRequestId: '2ee47065-afe2-433f-b88d-3280e25b5ae4',
  getRemainingTimeInMillis: [Function: getRemainingTimeInMillis]
}

via https://answers.netlify.com/t/could-not-access-user-from-context-inside-netlify-function/41801/6

The bad news is that there does seem to be some custom stuff in ClientContext in addition to the actual custom field being used. So maybe there is some post-processing going on 🤔 . Also, I'm not even sure if those things are "POJSOs" since they rendered as [Object] which suggests it's an instance of a class i.e. not something we can run through circe.

This is really annoying. I don't want to give up all the nice sharing though so I have to think what to do about it.

Edit: here's how they inject it into the lambda-local emulator: https://github.com/netlify/cli/blob/4e6a448bebf556d11e866d2231eb461db5486063/src/lib/functions/server.js#L12

@armanbilge
Copy link
Member

armanbilge commented Dec 22, 2021

Some additional notes on clientContext:

  1. custom.netlify will be available via Add support for custom field in ClientContext #117.
  2. The user object is actually just the payload of the JWT carried in the Authorization: Bearer token header. So, we can get at it ourselves without modifying our Context facade.
  3. The identity object, it's not clear to me if we can get at it another way—it would be great to experiment with Netlify and see if we can figure out how it's injected into the request (e.g. a header, environment variable, etc.). Otherwise, I'm inclined to ignore it until somebody complains 😝

@kubukoz
Copy link
Member Author

kubukoz commented Oct 5, 2022

clearly lost interest in this, I'll close for now but anyone can feel free to take whatever they want from this :)

@kubukoz kubukoz closed this Oct 5, 2022
@kubukoz kubukoz deleted the netlify-experiment branch October 5, 2022 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Netlify integration
2 participants