nervous-systems / cljs-lambda

Utilities around deploying Clojurescript functions to AWS Lambda
The Unlicense
310 stars 34 forks source link

Question: accessing aws-sdk #70

Closed danielytics closed 7 years ago

danielytics commented 7 years ago

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!!

moea commented 7 years ago

@danielytics No problem, this stuff is awkward at first. If you're using a version of the AWS SDK which supports the /clients/...-style require (which you will be if it's not throwing an exception), then your code should work - this works for me in a Clojurescript REPL against the most recent published aws-sdk in npm:

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

Is this this failing on Lambda only? What optimizations level is your project using? If it was an issue with advanced mode compilation renaming copyObject at the call site, I would expect it to work with the cljsjs guy, which includes externs (I'm assuming, I haven't used it). Trying it with none optimisations (which I'd recommend generally, unless you have a specific reason not to, or aren't depending on any straight-JS code) would be worthwhile, if applicable.

danielytics commented 7 years ago

Thanks very much. I was testing it only with lein cljs-lambda deploy, so I'll try it in local tests/repl and with optimisations none. I would also expect that if it was a renaming issue that the cljsjs library should fix it.

I've used plenty of js from cljs in the past without much issue, just not with nodejs or lambda, so I thought perhaps there was some detail I'm missing, but it seems there isn't. Thanks again for the quick reply; I'll report back once I've tried the above in case its useful to others (although it will be tomorrow morning utc)

moea commented 7 years ago

@danielytics FWIW I have a bunch of code deployed with cljs-lambda, using S3 via aws-sdk w/ optimizations none, as, I think, do others - so I'm more than happy to help in whatever way I can. If optimizations was advanced when you tested, it definitely wouldn't work locally or on Lambda - and the issue with cljsjs may just be some unrelated thing.

danielytics commented 7 years ago

Great, thanks! I imagine that's the case then: I need to use optimisations none and the cljsjs issue was unrelated. I'll confirm in the morning.

danielytics commented 7 years ago

Ok, that took a lot longer to get working than it should have... I have it working now though! Thanks again for the help, @moea.

Here's all of the mistakes I made:

(p/promise
  (fn [resolve]
    (.listObjects s3-client params
      (fn [e d] (resolve {:msg "Done" :data d :error e})))))
(async/map vector
  (for [record records]
    (process-record! record))

Each call to process-record! would return a channel. The AWS API callbacks simply put true onto their channel. I also successfully made multiple calls by giving each call its own channel and returning a vector of these channels and then calling flatten on the result of the for. There may be a cleaner way (for example, creating a promise for each call and then awaiting them maybe?)

Hopefully this is useful to somebody else!

moea commented 7 years ago

For my part, I'll update the template to use none optimizations.

danielytics commented 7 years ago

Or just make add a note in the README.