GithubHelp home page GithubHelp logo

nervous-systems / cljs-lambda Goto Github PK

View Code? Open in Web Editor NEW
312.0 312.0 34.0 192 KB

Utilities around deploying Clojurescript functions to AWS Lambda

License: The Unlicense

Clojure 93.23% HTML 1.60% JavaScript 0.93% Shell 4.24%
aws aws-lambda clojure clojurescript leiningen nodejs serverless

cljs-lambda's People

Contributors

aleksandersumowski avatar alessandrod avatar benzenwen avatar bhagany avatar crteal avatar dheidebrecht avatar dviramontes avatar honzabrecka avatar jetmind avatar joekiller avatar moea avatar paulbutcher avatar the-frey avatar vikeri avatar zrzka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cljs-lambda's Issues

Question: accessing aws-sdk

This is probably a dumb question, but I've been searching for a solution for a few hours now without luck.

I'm trying to access the S3 API, so that I can manipulate S3 objects from my lambda function and I can't figure out how to do this.

I've tried (def aws (nodejs/require "aws-sdk")) and (def s3 (nodejs/require "aws-sdk/clients/s3")) and while they seem to give me something back, I'm unsure what to do with this.

I've tried:

(def S3 (nodejs/require "aws-sdk/clients/s3"))
(def s3-client (new S3))

and this works in the sense that s3-client is now a non-nil object, but I've been unable to call functions on it.

eg

(.copyObject s3-client #js {:Bucket "bucket" :CopySource "bucket/source-key" :Key "destination-key"})

but it complains that s3-client does not have a copyObject field.

I then tried to use the cljsjs package for aws-sdk, but still no luck.

I assume this is standard nodejs interop from clojurescript, but this is my first time using nodejs (or lambda) from clojurescript and I can't seem to figure it out. What am I missing?

Any and all help is greatly appreciated!!

Async functions become unreponsive if Lambda global timeout occurs

When invoking an async promise if Lambda kills the function prior to the async promise being realized on subsequent calls the function is hung and doesn't enter the handler until either a new Lambda zip is uploaded or the Lambda function times out on AWS's infrastructure internally.

Using https://github.com/Type-Zero/promise-timeout-hang you can create two functions promise-hang and promise-timeout. When promise-timeout is invoked, the function will print Starting function and then throw a timeout error. Subsequent invocations behave the same.

Invoking promise-hang will print Staring function and then timeout via the Lambda timeout. Subsequent executions will not run the handler and you will not see Starting function. Basically at this point the function is useless.

I'm making this issue here because this is the top level where we encountered it but reading around the internet like http://theburningmonk.com/2016/05/aws-lambda-constant-timeout-when-using-bluebird-promise/ it appears that the problem lies in how bluebird is handling async promises.

Any insight is welcome. I'm not quite up to debugging this all the way down.

Function Does Not Appear In AWS Console

Should I be able to see the successfully deployed function in AWS console as "work magic"? I can deploy and invoke the function, but I see no trace of it in the console.

Lambda won't run when all functions were deployed at once

Weird issue happened today when I was quickly redeploying lambda functions to production.

  • One project
  • 13 lambda functions
  • Deployed all at once with lein cljs-lambda deploy without any other options

Some of these functions didn't work. Error message was:

{
  "errorMessage": "callback called with Error argument, but there was \
  a problem while retrieving one or more of its message, name, and stack"
}

I redeployed these functions again with the same command, but one by one. In other words, 13 commands:

  • lein cljs-lambda deploy FuncName
  • ...

Problem solved. All functions are working properly.

One issue can be parallelism. Will test tomorrow with :parallel 1 if this is the issue. Also tried to Google it and found: dawson-org/dawson-cli#1 It's worth to investigate it as well.

I've got environment where I can reproduce this issue (tried it, reproduced), will check what's wrong. Either permissions can be wrong in the ZIP file or there's issue when :parallel > 1.

The return of async-lambda-fn for a mock-context is not clear

While reading the README and using mock-context, it looks like that the result is unwrapped and I would have a vector with the values. At least that is what I expect from the comments on the README.

I could fetch the values, but I had to use:

(go
  (let [events (<! (cljs.core.async/into [] (testable 5 (mock-context))))]
    (println events) ; 
    ))

Handling of exceptions occurred outside of handler inside handler itself?

Description

Found this by accident and it's rather a question, thing for discussion, ...

I've got my custom lambda wrapper, which wraps async-lambda-fn:

(defn promise-lambda-fn
  [creds env event-schema promise-fn & rest]
  (async-lambda-fn
   (fn [ev context]
     (go
       (try
         (if-let [error (schema/check event-schema ev)]
           (fail! context (ex/bad-request error))
           (-> (apply promise-fn creds env ev context rest)
               (p/then #(succeed! context %))
               (p/catch #(fail! context %))))
         (catch :default e
           (fail! context (ex/internal-server-error "promise-lambda-fn" e))))))))

This wrapper is used in this way:

(def EventSchema
  {:principal-id ks/ErebosUserId ...})

(def ^:export handler
  (promise-lambda-fn
   (credentials)
   env-variables
   EventSchema
   invite-users))

It's a simple wrapper which is auto producing internal server error in case of exception or bad request in case if event is malformed. So far, so good. Works.

By accident, we did send atom value instead of proper message and lambda output is ...

{
  "errorMessage": "RequestId: 185a635d-3ffa-11e7-9ee6-33e1af8e69ed Process exited before completing request"
}

... with backtraces like ...

Error: No protocol method IEmptyableCollection.-empty defined for type object: [object Object]
    at Object.cljs$core$missing_protocol [as missing_protocol] (/var/task/target/erebos-lambda/cljs/core.cljs:272:4)

... and this happens because of ...

https://github.com/nervous-systems/cljs-lambda/blob/master/cljs-lambda/src/cljs_lambda/util.cljs#L25

... basically, js->clj :keywordize-keys true can't handle this particular event.

Question

What if I like to return specific response from lambda in this case? Something like our ex/bad-request (400) instead of this generic error, which produces 500 for example? Event is valid JSON and I personally don't like when 500 is returned instead of 400, just because this can't be parsed / keywordized.

I can live with this, but it's not good if I want to provide better status codes / response to API consumers.

Event

Sample event we did send by accident.

{
  "payload": {
    "message": {
      "state": {
        "meta": null,
        "cnt": 1,
        "root": {
          "edit": {},
          "bitmap": 512,
          "arr": [
            {
              "ns": null,
              "name": "value",
              "fqn": "value",
              "_hash": 305978217,
              "cljs$lang$protocol_mask$partition0$": 2153775105,
              "cljs$lang$protocol_mask$partition1$": 4096
            },
            "foo",
            null,
            null,
            null,
            null,
            null,
            null
          ]
        },
        "has_nil_QMARK_": false,
        "nil_val": null,
        "__hash": null,
        "cljs$lang$protocol_mask$partition0$": 16123663,
        "cljs$lang$protocol_mask$partition1$": 8196
      },
      "meta": null,
      "validator": null,
      "watches": {
        "meta": null,
        "cnt": 0,
        "arr": [],
        "__hash": -15128758,
        "cljs$lang$protocol_mask$partition0$": 16647951,
        "cljs$lang$protocol_mask$partition1$": 8196
      },
      "cljs$lang$protocol_mask$partition1$": 16386,
      "cljs$lang$protocol_mask$partition0$": 6455296
    }
  }
}

clojure.pprint not found (Travis CI)

Travis CI did update images today. Didn't find time to investigate this issue yet, maybe something will come to your mind immediately when you see it. There's this error when deploy is called:

clojure.lang.Compiler$CompilerException: java.lang.ClassNotFoundException: clojure.pprint, compiling:(leiningen/cljs_lambda/aws.clj:181:7)

Will check what's wrong tomorrow.

Consider a lumo version

Hello folks!

It would be really awesome to have something so that we could make cljs-lambda work in lumo.
Not only it would have faster startup but also the interactive REPL with lumo would be super easy to setup (compared to both nRepl and vanilla ClojureScript).

The problem that strikes me immediately is that there is a dependency on core.async that should be replaced by https://github.com/mfikes/andare. Also promesa could be replaced by vanilla Bluebird directly.

And....Yes...I see that would drastically change the project layout and it is probably worth a separate project.

In any case, thanks for your work on this, it is quite useful!

Incompatible with current ClojureScript (1.9.562)

The serverless template (and probably the main cljs-lambda template as well) is incompatible with the current (1.9.562) version of ClojureScript. Builds fail because java.lang.AssertionError: Assert failed: :nodejs target with :none optimizations requires a :main entry.

I've tried adding my .core namespace as the :main. Bizarrely, that leads to:

Exception in thread "main" java.lang.ClassNotFoundException:
[…]
Caused
     by: java.lang.ClassNotFoundException:<project-name>.core
        at
     java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at
     clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:69)
        at
     java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at
     clojure.lang.DynamicClassLoader.loadClass(DynamicClassLoader.java:77)
        at
     java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at
     java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:348)
        at
     clojure.lang.RT.classForName(RT.java:2168)
        at clojure.lang.RT.classForName(RT.java:2177)
        at
     clojure.lang.Compiler.resolveIn(Compiler.java:7145)
        at
     clojure.lang.Compiler.resolve(Compiler.java:7108)
        at
     clojure.lang.Compiler.analyzeSymbol(Compiler.java:7069)
        at
     clojure.lang.Compiler.analyze(Compiler.java:6648)
        ...
     30 more

(where <project-name> is the name of the project)

It looks like Clojure is trying to find the namespace instead of ClojureScript? I have no idea what's going on at this point.

Throw error if npm is not installed

First of all! Great work! Just deployed my first lambda function!

Not having npm in my path on shell load causes lein cljs-lambda deploy to silently fail, maybe an error would be great? I just lost half an hour because of this as I didn't expect node to be involved with deploying.

Consider defaulting `:process-shim` to false in template

In ClojureScript 1.9.854 a compiler option was added that shims the Node process object automatically (i.e., it is an opt-out feature). This means that if you are using environment variables in your Lambda functions they will get completely wiped out by the shim. It would be wonderful if the template automatically provided the compiler option with a value of false going forward.

Examples with aws cli used directly

Would be nice with creating/updating and invocation examples in the README where the aws cli is used directly. For instance, wrt invokation:

aws lambda invoke \
--invocation-type RequestResponse \
--function-name work-magic \
--region eu-west-1 \
--log-type Tail \
--payload '{"magic-word": "my-lambda-project-token", "spell":"delay-promise"}' \
outfile.txt

java.io.IOException: Cannot run program "aws": error=2, No such file or directory

When I run lein cljs-lambda invoke work-magic with any arguments it always returns this:

jims-first-cljs-lambda ♘ lein cljs-lambda invoke work-magic
aws lambda invoke /var/folders/41/b5ntx4mn2mv9qk3jyxnsnk_w0000gn/T/lambda-output1140111309954810249.json --function-name work-magic --payload  --log-type Tail --query LogResult --output text
java.io.IOException: Cannot run program "aws": error=2, No such file or directory
 at java.lang.ProcessBuilder.start (ProcessBuilder.java:1048)
    java.lang.Runtime.exec (Runtime.java:620)
    clojure.java.shell$sh.invokeStatic (shell.clj:113)
    clojure.java.shell$sh.doInvoke (shell.clj:79)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:652)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.cljs_lambda.aws$aws_cli_BANG_.invokeStatic (aws.clj:72)
    leiningen.cljs_lambda.aws$aws_cli_BANG_.doInvoke (aws.clj:69)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    clojure.core$partial$fn__4759.invoke (core.clj:2516)
    leiningen.cljs_lambda.aws$invoke_BANG_.invokeStatic (aws.clj:247)
    leiningen.cljs_lambda.aws$invoke_BANG_.invoke (aws.clj:235)
    leiningen.cljs_lambda$invoke.invokeStatic (cljs_lambda.clj:188)
    leiningen.cljs_lambda$invoke.invoke (cljs_lambda.clj:184)
    leiningen.cljs_lambda$cljs_lambda.invokeStatic (cljs_lambda.clj:230)
    leiningen.cljs_lambda$cljs_lambda.doInvoke (cljs_lambda.clj:214)
    clojure.lang.RestFn.invoke (RestFn.java:442)
    clojure.lang.Var.invoke (Var.java:388)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$partial_task$fn__5932.doInvoke (main.clj:272)
    clojure.lang.RestFn.applyTo (RestFn.java:139)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:29)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$apply_task.invokeStatic (main.clj:322)
    leiningen.core.main$apply_task.invoke (main.clj:308)
    leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:328)
    leiningen.core.main$resolve_and_apply.invoke (main.clj:324)
    leiningen.core.main$_main$fn__5998.invoke (main.clj:401)
    leiningen.core.main$_main.invokeStatic (main.clj:394)
    leiningen.core.main$_main.doInvoke (main.clj:391)
    clojure.lang.RestFn.invoke (RestFn.java:436)
    clojure.lang.Var.invoke (Var.java:388)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:646)
    clojure.main$main_opt.invokeStatic (main.clj:314)
    clojure.main$main_opt.invoke (main.clj:310)
    clojure.main$main.invokeStatic (main.clj:421)
    clojure.main$main.doInvoke (main.clj:384)
    clojure.lang.RestFn.invoke (RestFn.java:482)
    clojure.lang.Var.invoke (Var.java:401)
    clojure.lang.AFn.applyToHelper (AFn.java:171)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.main.main (main.java:37)
Caused by: java.io.IOException: error=2, No such file or directory
 at java.lang.UNIXProcess.forkAndExec (UNIXProcess.java:-2)
    java.lang.UNIXProcess.<init> (UNIXProcess.java:247)
    java.lang.ProcessImpl.start (ProcessImpl.java:134)
    java.lang.ProcessBuilder.start (ProcessBuilder.java:1029)
    java.lang.Runtime.exec (Runtime.java:620)
    clojure.java.shell$sh.invokeStatic (shell.clj:113)
    clojure.java.shell$sh.doInvoke (shell.clj:79)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:652)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.cljs_lambda.aws$aws_cli_BANG_.invokeStatic (aws.clj:72)
    leiningen.cljs_lambda.aws$aws_cli_BANG_.doInvoke (aws.clj:69)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    clojure.core$partial$fn__4759.invoke (core.clj:2516)
    leiningen.cljs_lambda.aws$invoke_BANG_.invokeStatic (aws.clj:247)
    leiningen.cljs_lambda.aws$invoke_BANG_.invoke (aws.clj:235)
    leiningen.cljs_lambda$invoke.invokeStatic (cljs_lambda.clj:188)
    leiningen.cljs_lambda$invoke.invoke (cljs_lambda.clj:184)
    leiningen.cljs_lambda$cljs_lambda.invokeStatic (cljs_lambda.clj:230)
    leiningen.cljs_lambda$cljs_lambda.doInvoke (cljs_lambda.clj:214)
    clojure.lang.RestFn.invoke (RestFn.java:442)
    clojure.lang.Var.invoke (Var.java:388)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$partial_task$fn__5932.doInvoke (main.clj:272)
    clojure.lang.RestFn.applyTo (RestFn.java:139)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:29)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$apply_task.invokeStatic (main.clj:322)
    leiningen.core.main$apply_task.invoke (main.clj:308)
    leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:328)
    leiningen.core.main$resolve_and_apply.invoke (main.clj:324)
    leiningen.core.main$_main$fn__5998.invoke (main.clj:401)
    leiningen.core.main$_main.invokeStatic (main.clj:394)
    leiningen.core.main$_main.doInvoke (main.clj:391)
    clojure.lang.RestFn.invoke (RestFn.java:436)
    clojure.lang.Var.invoke (Var.java:388)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:646)
    clojure.main$main_opt.invokeStatic (main.clj:314)
    clojure.main$main_opt.invoke (main.clj:310)
    clojure.main$main.invokeStatic (main.clj:421)
    clojure.main$main.doInvoke (main.clj:384)
    clojure.lang.RestFn.invoke (RestFn.java:482)
    clojure.lang.Var.invoke (Var.java:401)
    clojure.lang.AFn.applyToHelper (AFn.java:171)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.main.main (main.java:37)

I am running on mac 10.11.6 with Leiningen 2.7.1 on Java 1.8.0_121 Java HotSpot(TM) 64-Bit Server VM

How To Return JSON?

Hey, thanks again for making this. I'm wordering how to return normal JSON as my response seems to always look like a Clojure map even when I return (js-obj "a" 1 "b" true "c" "hello there, caller!") or #js{:a 1, :b true, :c nil}.

may need major version bump for cb -> ctx upgrade

I know the horse is already out of the stable but at least let this be the note on the door after the fact...

When the code db6a26e was updated to use callback. This had some unforeseen consequences that could severely affect the function of Lambda programs. In our case, we are using pg-pool for SQL connections. We were boggled when we were writing a new library that used the same sql calls seemed to wait until the idleTimeoutMillis hit (30 seconds in our case). The lambda code (sprinkled with tons of prn) gave us confidence that that part of the code was executing fine, including the sql query. However, downgrading cljs-lambda to 0.3.3 from 0.3.6 made the problem go away, because it doesn't wait for unfulfilled promises anymore.

I'm raising the issue because even as I directly asked for the update, I didn't realize it until we lost a morning on troubleshooting. Clearly the unfulfilled promises bug in our code needs to be fixed (I think it has to do with the pg-pool of connections) That being said, a bump in the bug fix number of the project version doesn't necessary convey how big the change really was. It was a "change the way all lambda by default respond to AWS" change.

Example project: sudo lein cljs-lambda deploy || Subprocess failed

Hi,

lein cljs-lamdba deploy failed on Ubuntu 14.04. Could you help me what is the problem?

~/mylambda/my-lambda-project# sudo lein cljs-lambda deploy
WARNING: You're currently running as root; probably by accident.
Press control-C to abort or Enter to continue as root.
Set LEIN_ROOT to disable this warning.

Compiling "target/my-lambda-project/my_lambda_project.js" from ["src"]...
Compiling "target/my-lambda-project/my_lambda_project.js" failed.
clojure.lang.ExceptionInfo: failed compiling file:target/my-lambda-project/cljs_lambda/macros.cljc {:file #object[java.io.File 0x6c2658b "target/my-lambda-project/cljs_lambda/macros.cljc"]}
at clojure.core$ex_info.invokeStatic(core.clj:4617)
at clojure.core$ex_info.invoke(core.clj:4617)
at cljs.compiler$compile_file$fn__3406.invoke(compiler.cljc:1372)
at cljs.compiler$compile_file.invokeStatic(compiler.cljc:1338)
at cljs.compiler$compile_file.invoke(compiler.cljc:1318)
at cljs.closure$compile_file.invokeStatic(closure.clj:473)
at cljs.closure$compile_file.invoke(closure.clj:464)
at cljs.closure$eval5143$fn__5144.invoke(closure.clj:540)
at cljs.closure$eval5079$fn__5080$G__5068__5087.invoke(closure.clj:430)
at cljs.closure$compile_from_jar.invokeStatic(closure.clj:522)
at cljs.closure$compile_from_jar.invoke(closure.clj:510)
at cljs.closure$eval5149$fn__5150.invoke(closure.clj:550)
at cljs.closure$eval5079$fn__5080$G__5068__5087.invoke(closure.clj:430)
at cljs.closure$compile_sources$iter__5264__5268$fn__5269.invoke(closure.clj:869)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.next(RT.java:688)
at clojure.core$next__4341.invokeStatic(core.clj:64)
at clojure.core$dorun.invokeStatic(core.clj:3033)
at clojure.core$doall.invokeStatic(core.clj:3039)
at clojure.core$doall.invoke(core.clj:3039)
at cljs.closure$compile_sources.invokeStatic(closure.clj:865)
at cljs.closure$compile_sources.invoke(closure.clj:854)
at cljs.closure$build.invokeStatic(closure.clj:1943)
at cljs.closure$build.invoke(closure.clj:1882)
at cljs.build.api$build.invokeStatic(api.clj:210)
at cljs.build.api$build.invoke(api.clj:198)
at cljs.build.api$build.invokeStatic(api.clj:201)
at cljs.build.api$build.invoke(api.clj:198)
at cljsbuild.compiler$compile_cljs$fn__5771.invoke(compiler.clj:60)
at cljsbuild.compiler$compile_cljs.invokeStatic(compiler.clj:59)
at cljsbuild.compiler$compile_cljs.invoke(compiler.clj:48)
at cljsbuild.compiler$run_compiler.invokeStatic(compiler.clj:168)
at cljsbuild.compiler$run_compiler.invoke(compiler.clj:122)
at user$eval5908$iter__5944__5948$fn__5949$fn__5967.invoke(form-init46941676171948885.clj:1)
at user$eval5908$iter__5944__5948$fn__5949.invoke(form-init46941676171948885.clj:1)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:521)
at clojure.core$seq__4357.invokeStatic(core.clj:137)
at clojure.core$dorun.invokeStatic(core.clj:3024)
at clojure.core$doall.invokeStatic(core.clj:3039)
at clojure.core$doall.invoke(core.clj:3039)
at user$eval5908.invokeStatic(form-init46941676171948885.clj:1)
at user$eval5908.invoke(form-init46941676171948885.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: java.lang.ClassNotFoundException: java.util.concurrent.CompletableFuture, compiling:(promesa/impl/promise.cljc:1:1) in file target/my-lambda-project/cljs_lambda/macros.cljc {:tag :cljs/analysis-error}
at clojure.core$ex_info.invokeStatic(core.clj:4617)
at clojure.core$ex_info.invoke(core.clj:4617)
at cljs.analyzer$error.invokeStatic(analyzer.cljc:580)
at cljs.analyzer$error.invoke(analyzer.cljc:576)
at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:2616)
at cljs.analyzer$analyze.invoke(analyzer.cljc:2605)
at cljs.compiler$emit_source.invokeStatic(compiler.cljc:1239)
at cljs.compiler$emit_source.invoke(compiler.cljc:1219)
at cljs.compiler$compile_file_STAR_$fn__3383.invoke(compiler.cljc:1290)
at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1154)
at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1145)
at cljs.compiler$compile_file_STAR_.invokeStatic(compiler.cljc:1279)
at cljs.compiler$compile_file_STAR_.invoke(compiler.cljc:1275)
at cljs.compiler$compile_file$fn__3406.invoke(compiler.cljc:1360)
... 60 more
Caused by: java.lang.ClassNotFoundException: java.util.concurrent.CompletableFuture, compiling:(promesa/impl/promise.cljc:1:1)
at clojure.lang.Compiler.load(Compiler.java:7391)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.loadResourceScript(RT.java:363)
at clojure.lang.RT.load(RT.java:453)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:436)
at promesa.core$eval8798$loading__5569__auto____8799.invoke(core.cljc:25)
at promesa.core$eval8798.invokeStatic(core.cljc:25)
at promesa.core$eval8798.invoke(core.cljc:25)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6916)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.loadResourceScript(RT.java:363)
at clojure.lang.RT.load(RT.java:453)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at cljs_lambda.macros$eval8792$loading__5569__auto____8793.invoke(macros.cljc:1)
at cljs_lambda.macros$eval8792.invokeStatic(macros.cljc:1)
at cljs_lambda.macros$eval8792.invoke(macros.cljc:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6916)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.loadResourceScript(RT.java:363)
at clojure.lang.RT.load(RT.java:453)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at cljs.analyzer$ns_side_effects$fn__2118.invoke(analyzer.cljc:2541)
at cljs.analyzer$ns_side_effects.invokeStatic(analyzer.cljc:2540)
at cljs.analyzer$ns_side_effects.invoke(analyzer.cljc:2513)
at cljs.analyzer$analyze_STAR_$fn__2154.invoke(analyzer.cljc:2603)
at clojure.lang.PersistentVector.reduce(PersistentVector.java:341)
at clojure.core$reduce.invokeStatic(core.clj:6544)
at clojure.core$reduce.invoke(core.clj:6527)
at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:2603)
at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:2593)
at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:2618)
... 69 more
Caused by: java.lang.ClassNotFoundException: java.util.concurrent.CompletableFuture
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:69)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at clojure.lang.DynamicClassLoader.loadClass(DynamicClassLoader.java:77)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:278)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForNameNonLoading(RT.java:2181)
at promesa.impl.promise$eval8804$loading__5569__auto____8805.invoke(promise.cljc:25)
at promesa.impl.promise$eval8804.invokeStatic(promise.cljc:25)
at promesa.impl.promise$eval8804.invoke(promise.cljc:25)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6916)
at clojure.lang.Compiler.load(Compiler.java:7379)
... 157 more
Subprocess failed

Question: growing number of lambda functions

Consider following scenario:

  • one project
  • 15 lambda functions (all functions are project related)
  • number of lambda functions is growing

Upon build & deploy, one ZIP file is created for all lambda functions and this file is uploaded for every lambda function. This takes space and has performance implications when lambda is cold and it also needs slightly more memory.

What's your recommended approach / best practice to cope with this? Are you splitting lambda functions into groups and one project per group? Or project per lambda? Or did I miss something and I shouldn't worry? Can't get out of my head that I would like to process my lambda functions with :advanced optimization per lambda, so, there will be specific and small ZIP file per lambda function, but I don't like one lambda function = one project approach.

My ZIP file is 0.5MB now.

Transit support?

I added transit as an optional format to send and/or receive data. Is this something you have plans to support? Would you mind if I created a pull request for it?

Add support for "new" Lambda env variables

Documentation here.

Example:

:cljs-lambda {
  :defaults {:env {:table-name "abc"}}
  :functions [{
    :name "abc"
    :invoke "def"
    :env {:extra-stuff "xyz"}}]}

:defaults contains env variables for all functions. :env inside :functions list is merged with environment variables from :defaults.

Docs quote: environment variables must start with a-zA-Z and can contain a-ZA-Z0-9_.

We will transform keywords in this way:

  • upper case
  • all not allowed characters replaced with _

We will not check these variables and we will let aws cli to fail if they do not meet requirements.

We need to add this to:

  • create function
  • update function

Update function is little bit harder, because we have to get function configuration (if it exists) and compare it. We can do it in the same way as we did in #48.

Sounds good?

VPC configuration

There's no way how to configure VPC for lambda function. We do use it heavily now and we have to do it manually.

There's --vpc-config in AWS CLI for lambda create-function and lambda update-function-configuration. Documentation says:

   --vpc-config (structure)
      If your Lambda function accesses resources in  a  VPC,  you  provide
      this parameter identifying the list of security group IDs and subnet
      IDs. These must belong to the same VPC. You must  provide  at  least
      one security group and one subnet ID.

   Shorthand Syntax:

      SubnetIds=string,string,SecurityGroupIds=string,string

So, my proposal is to add :vpc-config which will accept shorthand syntax. So, Lambda configuration can look like:

{:name ...
 :invoke ...
 :role ...
 :vpc-config "SubnetIds=a,b,SecurityGroupIds=d,e"}

Or we can split it to :vpc-subnet-ids and :vpc-security-group-ids. Depends.

The first one is easier to maintain in case AWS CLI will be changed in the future. Which one do you like more? I'll create pull request, just want to discuss it before I really do it.

Zip file not showing up

Not sure why. Using open JDK8 on Arch Linux.

$ java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
$ lein version
Leiningen 2.5.1 on Java 1.8.0_45 Java HotSpot(TM) 64-Bit Server VM
Compiling ClojureScript.
Writing index to /home/me/project/cljs/lambda-ami-lookup/out/index.js
Adding files from /home/me/project/cljs/lambda-ami-lookup/out
Adding files from /home/me/project/cljs/lambda-ami-lookup/node_modules
Writing zip to /home/me/project/cljs/lambda-ami-lookup/out/lambda-ami-lookup.zip
Registering handler index.lambda_ami_lookup_core_work_magic for function work-magic
aws lambda get-function --function-name work-magic
aws lambda create-function --function-name work-magic --zip-file fileb:////home/me/project/clme nodejs --handler index.lambda_ami_lookup_core_work_magic --role lambda-iam-AMILookupRole-16JFOYHF

Error parsing parameter '--zip-file': Unable to load paramfile fileb:///home/me/project/cljs] No such file or directory: '/home/me/project/cljs/lambda-ami-lookup/out/lambda-ami-lookup.

I wish I could give you more :(

Does the serverless support imply support for e.g. GCP Functions, Azure Cloud Functions?

I haven't used the Serverless framework before, but my understanding is that one of it's biggest selling points is abstracting away the differences between GCP Functions, Azure Cloud Functions and AWS Lambda, making it possible to run almost the same codebase across different providers. Does that apply to projects made with this template too? Am I misunderstanding what Serverless provides, or what portions of it this project uses? (Is it just for uploading to AWS Lambda?)

Without advanced optimizations, deploys non-working function

I'm working on a function that will depend on a node.js package. Guided by the comment here:

Note: Under Node.js there is little reason to use advanced optimizations. While advanced optimizations does apply performance related optimizations, these are now largely obviated by optimizations present in modern JavaScript virtual machines like V8, SpiderMonkey, and JavaScriptCore. For Node.js, :simple or :none optimizations suffice and using them removes the need for extra steps like supplying an externs file.

I decided to try deploying the cljs-lambda example function using :simple optiimizations. But the deployed function fails. (It took me a while to figure out the nature of the failure, but base.js is apparently attempting to load a non-existent deps.js file.)

ReferenceError when using :cljs-lambda {:compiler ...} and :optimizations :advanced

Here's a better description of the ReferenceError bug I tried to fix as part of #77.

To reproduce the bug, create a new project with the serverless-cljs template, then change :optimizations to :advanced and finally try to run the output: you get the ReferenceError.

The serverless-cljs template creates the following project.clj:

(defproject bar "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure       "1.8.0"]
                [org.clojure/clojurescript "1.8.51"]
                [io.nervous/cljs-lambda    "0.3.5"]]
:plugins [[lein-npm                    "0.6.2"]
            [io.nervous/lein-cljs-lambda "0.6.5"]]
:npm {:dependencies [[serverless-cljs-plugin "0.1.2"]]}
:cljs-lambda {:compiler
                {:inputs  ["src"]
                :options {:output-to     "target/bar/bar.js"
                        :output-dir    "target/bar"
                        :target        :nodejs
                        :language-in   :ecmascript5
                        :optimizations :advanced}}})

As you can see it uses :cljs-lambda {:compiler ...} to configure the compiler instead of specifying a :cljsbuild key. That leads lein-cljs-lambda to invoking the clojurescript compiler directly instead of going through cljsbuild, which ultimately causes this issue.

Below is a transcript of how I can reproduce the bug:

Script started on Wed May  3 20:05:34 2017
alessandro@june:/tmp$ lein new serverless-cljs bar
Generating fresh 'lein new' serverless-cljs project.
alessandro@june:/tmp$ cd b�ar/
alessandro@june:/tmp/bar$ sed -i -e s/:none/:advanced/ project.clj 
alessandro@june:/tmp/bar$ grep optimizations project.clj 
                        :optimizations :advanced}}})
alessandro@june:/tmp/bar$ lein deps
[email protected] /private/tmp/bar
└─┬ [email protected] 
├── [email protected] 
├── [email protected] 
└─┬ [email protected] 
    └── [email protected] 

alessandro@june:/tmp/bar$ serverless package
Serverless: Targeting /private/tmp/bar/.serverless/bar.zip
Serverless: Packaging service...
Serverless: Executing "lein update-in :cljs-lambda assoc :functions '[{:name "bar-dev-echo" :invoke bar.core/echo}]' -- cljs-lambda build :output /private/tmp/bar/.serverless/bar.zip :quiet"
Serverless: Returning artifact path /private/tmp/bar/.serverless/bar.zip�[39m
alessandro@june:/tmp/bar$ unzip -q -d aws_zip .serverless/bar.zip 
alessandro@june:/tmp/bar$ cd aws_zip
alessandro@june:/tmp/bar/aws_zip$ cat index.js 

require("./target/bar/bar.js");

exports.bar_core_SLASH_echo = bar.core.echo;

alessandro@june:/tmp/bar/aws_zip$ node index.js 
/private/tmp/bar/aws_zip/index.js:4
exports.bar_core_SLASH_echo = bar.core.echo;
                            ^

ReferenceError: bar is not defined
    at Object.<anonymous> (/private/tmp/bar/aws_zip/index.js:4:31)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:423:7)
    at startup (bootstrap_node.js:147:9)
    at bootstrap_node.js:538:3
alessandro@june:/tmp/bar/aws_zip$ exit
exit

Script done on Wed May  3 20:08:35 2017

using lambda role in sns/publish-topic!

Hello,

I'm trying to make a lambda endpoint that sends messages to SNS...

(ns parsehook.core
  (:require [cljs-lambda.util :refer [async-lambda-fn]]
            [fink-nottle.sns :as sns]
            [cljs.core.async :refer [chan <!]]
            [eulalie.instance-data :refer [default-iam-credentials!]])
  (:require-macros [cljs.core.async.macros :refer [go]]))

(def ^:export work-magic
  (async-lambda-fn
   (fn [event context]
     (go
       (<! (sns/publish-topic!
             (<! (default-iam-credentials!))
             "arn:aws:sns:us-west-2:785400771354:parsehook-qa"
             (.stringify js/JSON event)))))))

does the above usage of default-iam-credentials look correct? When I do it this way and invoke the function I get:

{:errorMessage "Task timed out after 3.00 seconds"}

When i pass creds as a regular map with {:access-key, :secret-key and :region} it works. But I'd prefer to use the IAM role if possible. Not sure if my script is throwing an exception when I try to use (default-iam-credentials!)... I'm unclear how error reporting works in Lambda, beyond returning an Error via the channel.

I am not in the default region, not sure if I need to assoc region into the default-iam-credential or something. I'm totally new to lambda, just trying to figure it out. Thanks!

Variables validator

Story

Our projects heavily rely on :cljs-lambda :defaults :env. Also, we have production variables set in the :production profile (profiles.clj). Sometimes we forget to ...

  • add env variable to profiles.clj,
  • replace TODO with production value (waiting for CF template changeset to be deployed),
  • set env variable (for example #=(eval (System/getenv "STRIPE_API_KEY_SECRET"))) and dev value is deployed to production,
  • ...

Repetitive tasks, prone to error, ... Boring, no one wants to do them.

Idea

My idea is to enhance cljs-lambda plugin with some validation task. Something like:

  • Are all env keys in project.clj set in profiles.clj?
  • Does profile.clj contain TODO as a value for any key?
  • Do these values differ?

Do you think it's good enhancement for cljs-lambda plugin or we should create new plugin? Honestly, not sure if I can come up with generic validator, with enough parameters, to suit needs for lot of people. Our cases are described above, but there can be lot of other cases. Also not sure if plugin can read all these combinations from project & profile files without merging, etc. Didn't test it yet, just thinking aloud.

Error when running tutorial

Running the commands in the readme, I get the following error trying to invoke the Lambda function. I'm not 100% on the issue, but I suspect it could be that the payload doesn't have single quoted stings around it?

$lein cljs-lambda invoke work-magic '{"variety": "black"}'
aws lambda invoke /var/folders/5s/0stnfr1s5tjdvcjfy_xdkdb00000gn/T/lambda-output4490944749712244058.json --function-name work-magic --payload {"variety": "black"} --log-type Tail --query LogResult
java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.String
 at base64_clj.core$decode.invoke (core.clj:125)
    base64_clj.core$decode.invoke (core.clj:123)
    leiningen.cljs_lambda.aws$invoke_BANG_.invoke (aws.clj:98)
    leiningen.cljs_lambda$invoke.doInvoke (cljs_lambda.clj:93)
    clojure.lang.RestFn.applyTo (RestFn.java:142)
    clojure.core$apply.invoke (core.clj:632)
    leiningen.cljs_lambda$cljs_lambda.doInvoke (cljs_lambda.clj:121)
    clojure.lang.RestFn.invoke (RestFn.java:464)
    clojure.lang.Var.invoke (Var.java:394)
    clojure.lang.AFn.applyToHelper (AFn.java:165)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invoke (core.clj:632)
    leiningen.core.main$partial_task$fn__6030.doInvoke (main.clj:261)
    clojure.lang.RestFn.applyTo (RestFn.java:139)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:29)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invoke (core.clj:632)
    leiningen.core.main$apply_task.invoke (main.clj:311)
    leiningen.core.main$resolve_and_apply.invoke (main.clj:317)
    leiningen.core.main$_main$fn__6096.invoke (main.clj:390)
    leiningen.core.main$_main.doInvoke (main.clj:383)
    clojure.lang.RestFn.invoke (RestFn.java:457)
    clojure.lang.Var.invoke (Var.java:394)
    clojure.lang.AFn.applyToHelper (AFn.java:165)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invoke (core.clj:630)
    clojure.main$main_opt.invoke (main.clj:316)
    clojure.main$main.doInvoke (main.clj:421)
    clojure.lang.RestFn.invoke (RestFn.java:512)
    clojure.lang.Var.invoke (Var.java:409)
    clojure.lang.AFn.applyToHelper (AFn.java:178)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.main.main (main.java:37)

How to develop locally, preferably in a REPL, (question rather than issue)

I was stoked to see this already existed. I have a couple of questions regarding implementation however, that to me were not entierly clear from the readme or blog posts. I would like to test the lambda function locally before deploying it. I got it working with basic examples in the tests. But when I started using callbacks (of external node-libs) in the lambda function the test finished before the callback came back, (Naturally, since the function returned while the callback was still firing).

Any details on when to use lambda/succeed! vs returning go channels, I understood that async-lambda-fn was supposed to be better than wrap-lambda-fn, but did not understand how?

Ultimately, any tips on how would I set up a good REPL-workflow with this?

Add example of how to set a region in project.clj

I've tried :region in [:cljs-lambda :defaults], and in [:cljs-lambda] and they both don't seem to be using the supplied region, and are instead taking the one from my profile. Should I be putting it somewhere else?

Publishing & aliasing multiple functions

It's a pain to :publish & :alias lot of functions (same for all of them, like build-XX where XX is Travis build number for example) without additional scripting. Is anyone working on this? If not, I need it and I would like to help with this feature if you're interested.

Serverless template does not include doo

Apologies if this is obvious, but I can't get doo to work with the Serverless project file. I see that the template is different. Should I not use doo with Serverless? Or is there some way of doing it that I don't understand? If the former, please advise on testing strategies. Thanks!

Can't use mock-context on a REPL

While using this in ClojureScript:
(:require [cljs-lambda.util :refer [mock-context]])

I can use mock-context and it is #<function cljs_lambda$util$mock_context(){...

While on a ClojureScript REPL, using the same require, mock-context is always nil. I have tried both Rhino and Node.js REPLs. I can import and use async-lambda-fn, for instance, but not mock-context.

Simple that require line above followed by (mock-context) will raise an error, because mock-context is nil.

aws lambda without optimizations

Hi, I'm trying to create a lambda using this cljs-lambda and https://github.com/r0man/sqlingvo.node in order to communicate with an postgres RDS db. I'm hitting a few problems which I think are related to advanced optimizations. I've tried to set the optimzation to none but I can't get a working lambda. I'm getting errors like 'Unable to import module 'index'. Are there any examples of cljs-lambda packages without advanced optimizations so I can compare to my project and rule this out?

Transition to using Node v4.3 callback param

From the AWS docs:

If you previously created Lambda functions using Node.js runtime v0.10.42, you used one of the context object methods (done(), succeed(), and fail()) to terminate your Lambda function. In Node.js runtime v4.3, these methods are supported primarily for backward compatibility. We recommend you use the callback (see Using the Callback Parameter).

There's more on their reasoning in the announcement.

Errors and Invoking Function Always Returns :errorMessage "Your magic word is garbage"

I have set up the aws cli and configured the user role. However, when I invoke the function it always returns ":errorMessage "Your magic word is garbage""

I run lein cljs-lambda invoke work-magic '{"spell": "delay-promise", "msecs": 500, "magic-word": "my-lambda-project-token"}'

output:

aws lambda invoke /var/folders/41/b5ntx4mn2mv9qk3jyxnsnk_w0000gn/T/lambda-output7320674589193443835.json --function-name work-magic --payload {"spell": "delay-promise", "msecs": 500, "magic-word": "my-lambda-project-token"} --log-type Tail --query LogResult --output text
START RequestId: e92005f3-b9f7-11e7-9c52-0d9eab9ceec1 Version: $LATEST
2017-10-26T02:46:49.182Z e92005f3-b9f7-11e7-9c52-0d9eab9ceec1 {"errorMessage":"Your magic word is garbage","errorType":"Error","stackTrace":["Function. (/var/task/target/mylambylam/mylambylam/core.cljs:41:12)","Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (/var/task/target/mylambylam/cljs/core.cljs:3566:18)","cljs$core$apply (/var/task/target/mylambylam/cljs/core.cljs:3557:1)","/var/task/target/mylambylam/cljs_lambda/util.cljs:40:24","promesa.impl.proto._promise.function (/var/task/target/mylambylam/promesa/impl/promise.cljc:199:8)","promesa$impl$proto$_promise (/var/task/target/mylambylam/promesa/impl/proto.cljc:41:1)","promesa$core$promise (/var/task/target/mylambylam/promesa/core.cljc:71:4)","Function.cljs_lambda.util.invoke_async.cljs$core$IFn$_invoke$arity$variadic (/var/task/target/mylambylam/cljs_lambda/util.cljs:36:4)","cljs_lambda$util$invoke_async (/var/task/target/mylambylam/cljs_lambda/util.cljs:35:1)","/var/task/target/mylambylam/cljs_lambda/util.cljs:110:15","cljs_lambda.util.wrap_lambda_fn.G__12212__delegate (/var/task/target/mylambylam/cljs_lambda/util.cljs:25:6)","cljs_lambda.util.wrap_lambda_fn.G__12212 (/var/task/target/mylambylam/cljs_lambda/util.cljs:20:3)","invoke (/var/runtime/node_modules/awslambda/index.js:288:5)","InvokeManager.start (/var/runtime/node_modules/awslambda/index.js:150:9)","Object. (/var/runtime/node_modules/awslambda/index.js:482:52)"]}
END RequestId: e92005f3-b9f7-11e7-9c52-0d9eab9ceec1
REPORT RequestId: e92005f3-b9f7-11e7-9c52-0d9eab9ceec1 Duration: 390.49 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 71 MB

{:errorMessage "Your magic word is garbage",
:errorType "Error",
:stackTrace
["Function. (/var/task/target/mylambylam/mylambylam/core.cljs:41:12)"
"Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (/var/task/target/mylambylam/cljs/core.cljs:3566:18)"
"cljs$core$apply (/var/task/target/mylambylam/cljs/core.cljs:3557:1)"
"/var/task/target/mylambylam/cljs_lambda/util.cljs:40:24"
"promesa.impl.proto._promise.function (/var/task/target/mylambylam/promesa/impl/promise.cljc:199:8)"
"promesa$impl$proto$_promise (/var/task/target/mylambylam/promesa/impl/proto.cljc:41:1)"
"promesa$core$promise (/var/task/target/mylambylam/promesa/core.cljc:71:4)"
"Function.cljs_lambda.util.invoke_async.cljs$core$IFn$_invoke$arity$variadic (/var/task/target/mylambylam/cljs_lambda/util.cljs:36:4)"
"cljs_lambda$util$invoke_async (/var/task/target/mylambylam/cljs_lambda/util.cljs:35:1)"
"/var/task/target/mylambylam/cljs_lambda/util.cljs:110:15"
"cljs_lambda.util.wrap_lambda_fn.G__12212__delegate (/var/task/target/mylambylam/cljs_lambda/util.cljs:25:6)"
"cljs_lambda.util.wrap_lambda_fn.G__12212 (/var/task/target/mylambylam/cljs_lambda/util.cljs:20:3)"
"invoke (/var/runtime/node_modules/awslambda/index.js:288:5)"
"InvokeManager.start (/var/runtime/node_modules/awslambda/index.js:150:9)"
"Object. (/var/runtime/node_modules/awslambda/index.js:482:52)"]}

java.util.concurrent.ExecutionException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('V' (code 86)): Expected space separating root-level values

When I run the lein cljs-commands it seems to work, but I get this error in the console:

java.util.concurrent.ExecutionException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('V' (code 86)): Expected space separating root-level values
 at [Source: java.io.StringReader@174e1b69; line: 1, column: 3]
 at java.util.concurrent.FutureTask.report (FutureTask.java:122)
    java.util.concurrent.FutureTask.get (FutureTask.java:192)
    sun.reflect.NativeMethodAccessorImpl.invoke0 (NativeMethodAccessorImpl.java:-2)
    sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke (Method.java:498)
    clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:93)
    clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:313)
    leiningen.cljs_lambda.aws$do_functions_BANG_.invokeStatic (aws.clj:208)
    leiningen.cljs_lambda.aws$do_functions_BANG_.invoke (aws.clj:197)
    leiningen.cljs_lambda.aws$deploy_BANG_.invokeStatic (aws.clj:211)
    leiningen.cljs_lambda.aws$deploy_BANG_.invoke (aws.clj:210)
    leiningen.cljs_lambda$deploy.invokeStatic (cljs_lambda.clj:177)
    leiningen.cljs_lambda$deploy.invoke (cljs_lambda.clj:172)
    leiningen.cljs_lambda$cljs_lambda.invokeStatic (cljs_lambda.clj:230)
    leiningen.cljs_lambda$cljs_lambda.doInvoke (cljs_lambda.clj:214)
    clojure.lang.RestFn.invoke (RestFn.java:425)
    clojure.lang.Var.invoke (Var.java:383)
    clojure.lang.AFn.applyToHelper (AFn.java:156)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$partial_task$fn__5932.doInvoke (main.clj:272)
    clojure.lang.RestFn.applyTo (RestFn.java:139)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:29)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    leiningen.core.main$apply_task.invokeStatic (main.clj:322)
    leiningen.core.main$apply_task.invoke (main.clj:308)
    leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:328)
    leiningen.core.main$resolve_and_apply.invoke (main.clj:324)
    leiningen.core.main$_main$fn__5998.invoke (main.clj:401)
    leiningen.core.main$_main.invokeStatic (main.clj:394)
    leiningen.core.main$_main.doInvoke (main.clj:391)
    clojure.lang.RestFn.invoke (RestFn.java:421)
    clojure.lang.Var.invoke (Var.java:383)
    clojure.lang.AFn.applyToHelper (AFn.java:156)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:646)
    clojure.main$main_opt.invokeStatic (main.clj:314)
    clojure.main$main_opt.invoke (main.clj:310)
    clojure.main$main.invokeStatic (main.clj:421)
    clojure.main$main.doInvoke (main.clj:384)
    clojure.lang.RestFn.invoke (RestFn.java:457)
    clojure.lang.Var.invoke (Var.java:394)
    clojure.lang.AFn.applyToHelper (AFn.java:165)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.main.main (main.java:37)
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('V' (code 86)): Expected space separating root-level values
 at [Source: java.io.StringReader@174e1b69; line: 1, column: 3]
 at com.fasterxml.jackson.core.JsonParser._constructError (JsonParser.java:1487)
    com.fasterxml.jackson.core.base.ParserMinimalBase._reportError (ParserMinimalBase.java:518)
    com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar (ParserMinimalBase.java:447)
    com.fasterxml.jackson.core.base.ParserMinimalBase._reportMissingRootWS (ParserMinimalBase.java:463)
    com.fasterxml.jackson.core.json.ReaderBasedJsonParser._verifyRootSpace (ReaderBasedJsonParser.java:1236)
    com.fasterxml.jackson.core.json.ReaderBasedJsonParser._parsePosNumber (ReaderBasedJsonParser.java:886)
    com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken (ReaderBasedJsonParser.java:680)
    cheshire.parse$parse.invokeStatic (parse.clj:87)
    cheshire.parse$parse.invoke (parse.clj:85)
    cheshire.core$parse_string.invokeStatic (core.clj:153)
    cheshire.core$parse_string.invoke (core.clj:142)
    cheshire.core$parse_string.invokeStatic (core.clj:149)
    cheshire.core$parse_string.invoke (core.clj:142)
    leiningen.cljs_lambda.aws$get_function_configuration_BANG_.invokeStatic (aws.clj:150)
    leiningen.cljs_lambda.aws$get_function_configuration_BANG_.invoke (aws.clj:143)
    leiningen.cljs_lambda.aws$deploy_function_BANG_.invokeStatic (aws.clj:187)
    leiningen.cljs_lambda.aws$deploy_function_BANG_.invoke (aws.clj:185)
    leiningen.cljs_lambda.aws$deploy_BANG_$fn__1213.invoke (aws.clj:214)
    clojure.lang.AFn.applyToHelper (AFn.java:154)
    clojure.lang.AFn.applyTo (AFn.java:144)
    clojure.core$apply.invokeStatic (core.clj:646)
    clojure.core$with_bindings_STAR_.invokeStatic (core.clj:1881)
    clojure.core$with_bindings_STAR_.doInvoke (core.clj:1881)
    clojure.lang.RestFn.invoke (RestFn.java:442)
    leiningen.cljs_lambda.aws$do_functions_BANG_$iter__1187__1191$fn__1192$fn__1193$fn__1194.invoke (aws.clj:205)
    clojure.lang.AFn.call (AFn.java:18)
    java.util.concurrent.FutureTask.run (FutureTask.java:266)
    java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:617)
    java.lang.Thread.run (Thread.java:745)

Print shell commands etc. to stderr

Output of lein cljs-lambda invoke isn't machine-readable due to the commands being echoed, etc. - we should probably print that stuff to stderr and/or add a :quiet option

Tracing config support

There's new switch in the awscli for update-function-configuration:

       --tracing-config (structure)
          The parent object that contains your function's tracing settings.

       Shorthand Syntax:

          Mode=string

       JSON Syntax:

          {
            "Mode": "Active"|"PassThrough"
          }

It's AWS X-Ray related and I would like to add support for it. Proposal is:

:defaults {:tracing :active}
  • :tracing can be used in :defaults and per lambda function
  • :tracing values can be :active or :passthrough
  • If :tracing is not provided, it defaults to :passthrough

It defaults to :passthrough, because it's a default value for all AWS Lambda functions (checked via awscli).

Okay?

Boot support?

Would you be open to including boot support? I am happy to submit a PR for this feature.

Use deflambda and defgateway docstring for lambda description

Hi again!

This is a small improvement and feature request I noticed would be awesome.
The docstring can be used for the AWS Lambda description.

At the moment they are empty (I am using serverless-cljs-plugin).

Thanks as usual for your work and this plugin ;)

Example with dynamodb

It would be enlightening to see how to idiomatically use async-lambda-fn together with external calls, for example to dynamodb. The callbacks there return either an Error or the result. I'm sure channels can be used to an advantage here, but I'll need an example to really grasp it.

Strategy for keywordizing keys and values?

Hi! What's the strategy for keywordizing keys and values?
If I invoke with eg
lein cljs-lambda invoke myFn '{"foo":"bar"}',,
it arrives in my deflambda as {:foo :bar}.

This seems a bit too magical to me, as I might want {"foo" "bar"}, {:foo "bar"} or maybe even {"foo" :bar}. It probably should be up to the implementer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.