Fork me on GitHub
#pedestal
<
2017-03-28
>
jimmy05:03:08

is this example relevant ? or we all change to use component reload workflow now ? https://github.com/pedestal/samples/tree/master/auto-reload-server

ddeaguiar13:03:10

@nxqd That sample repository is deprecated.

ddeaguiar13:03:26

> These samples are deprecated All maintained samples can be found within the Pedestal repository, with further documentation on the Pedestal site.

ddeaguiar13:03:28

Usage of Stuart Sierra’s reloaded workflow is great when using his Component library although the ideas are more generally applicable

ddeaguiar13:03:36

Not sure about that. I wonder how many folks inadvertently make that mistake

jimmy13:03:30

I do use reloaded workflow, but I always think it would be great that new comer knows about it and it should be integrated by default within pedestal.

ddeaguiar13:03:42

I tend to use component-based systems quite frequently but I’ve also found Pedestal’s support for reloading routes while developing quite good and sufficient for most situations.

ddeaguiar13:03:06

I think the goal with Pedestal is that, once you start a server, you shouldn’t need to restart it to make changes to your handlers/interceptors

ddeaguiar13:03:26

Paul talked about this somewhere. I can’t recall off-hand

ddeaguiar13:03:26

BTW, service-tools does include ns watching and reloading utilities (https://github.com/pedestal/pedestal/blob/master/service-tools/src/io/pedestal/service_tools/dev.clj), but I have to admit that I don’t really use them

ddeaguiar14:03:20

It’s included as a dev dependency in projects built with the pedestal-service template

jimmy14:03:42

thanks guys 😄

ddeaguiar14:03:06

I’d like to update that guide at some point. I’m now using component.repl vs reloaded.repl

ddeaguiar14:03:49

I prefer it because it supports the convention of using a dev ns

deg14:03:29

Another newbie-biting error message: I'm not sure if this one is best caught by Vase or Datomic... I'm populating a database from my client, and am passing in a load of generated [entity attribute value] triples. One of them has a null value, but the error message does not help me know which one: `ERROR i.p.http.impl.servlet-interceptor - {:msg "Dev interceptor caught an exception; Forwarding it as the response.", :line 313} clojure.lang.ExceptionInfo: Interceptor Exception: java.lang.IllegalArgumentException: :db.error/nil-value Nil is not a legal value at clojure.core$ex_info.invokeStatic(core.clj:4725) at clojure.core$ex_info.invoke(core.clj:4725) at io.pedestal.interceptor.chain$throwable__GT_ex_info.invokeStatic(chain.clj:33) ...`

ddeaguiar14:03:49

That’s best caught by Datomic imo, but i recall seeing the tuple in question in the error message when that’s happened to me. It’s been a while, though

jimmy14:03:53

@deg I'm not sure it's better for Datomic to print out which value that has the failed datum. In this case, you can catch the exception and print failed datum out if you want.

deg15:03:47

Where is the right place to catch the exception? So far, all of my server-side "code" has been just data in the .edn file.

ddeaguiar15:03:23

You can define an interceptor using the #vase/intercept tagged literal

ddeaguiar15:03:50

or define it externally and include it, IIRC

ddeaguiar15:03:17

Be aware of the rough edges around interceptors defined with #vase/intercept. I think they’re harder to debug when they error. That’s something which is being looked into.

deg15:03:47

Hmm, I'm going to pass for the moment... I can find the error in my client log, so I don't have a pressing need right now and figuring out where and what to catch in the first interceptor that I've not yet written is getting too deep down the shaven yak. Thanks, though.

deg15:03:01

Is there an example somewhere of doing an upsert via Vase?

ddeaguiar15:03:17

I can’t say for sure but I presume any of the current samples capture that.

ddeaguiar15:03:30

but I think you need to specify the :db/id of your entity when you are transacting changes

deg15:03:50

Yup, I understand that, but am hitting snags in seeing exactly what to do. How do I know the :db/id if I don't even know if the entity exists yet?

mtnygard15:03:33

@deg I'd recommend using an attribute that uniquely identifies the entity in question, but not the db/id.

mtnygard15:03:50

http://docs.datomic.com/identity.html talks about uniqueness and identity attributes. The short version is that using :db/unique :db.unique/identity on an attribute means that any new assertions with that value for the attribute get merged into any existing entity. It's upsert for free, basically.

ddeaguiar15:03:01

Thanks for clarifying @mtnygard. I thought I had seen some code in Vase that assoc’d a :db/id if it wasn’t present in a submitted entity.

mtnygard15:03:28

Hmm... let me double-check that.

deg15:03:06

I'm hitting a blind spot here. According to the your_first_api doc, I need to add :db/id in my my transact properties, iff I don't want to create a new object. But, my client does not know if it needs to create a new object then ....

ddeaguiar15:03:10

yeah, that won’t work

ddeaguiar15:03:20

@mtnygard try it with the sample Petstore app

ddeaguiar15:03:39

I get this error message when I try to upsert

Caused by: datomic.impl.Exceptions$IllegalStateExceptionInfo: :db.error/unique-conflict Unique conflict: :pet-store.pet/id, value: 1 already held by: 17592186045422 asserted for: 17592186045424

mtnygard15:03:37

I'm looking at com.cognitect.vase.actions/process-id right now. Basically, it says if there's no db/id on the transaction, then a tempid will be assigned. If an entity has a tempid and uses the same value for a :db.unique/identity attribute, it is supposed to be merged.

mtnygard15:03:59

I think the issue is that pet-store uses :db.unique/value when it should use :db.unique/identity.

deg15:03:09

Another, maybe related question. I had tried a poor-man's upsert (get, followed by client-side logic), but the way I did it needed to get the :db/id of my newly-created entity. According to your_first_api, The JSON packet above, because it does not contain a :db/id field refers to a new entity and the return value from the Vase service will return its newly created :db/id. But, this does not seem to be the case, at least not fully usefully ...

mtnygard15:03:17

(The schema-tx literal uses :unique instead of :identity)

deg15:03:54

It returns a map with :whitelist that contains the new entity, but without the :db/id and :transactions which does have the id embedded. but deep enough that it feels wrong to rely on the format as authoritative. Is the line in the doc just wrong, or something deeper?

mtnygard15:03:25

@deg Give me a few minutes to dig into that.

ddeaguiar15:03:20

@mtnygard changing the attribute from :unique to :identity in the petstore example worked. Thanks for catching that, I should have known!

mtnygard15:03:59

That's a defect in the sample.

ddeaguiar15:03:40

I didn’t realize the code in the transact action conditionally assoc’d a :db/id based on the presence of an identity attribute. That totally makes sense and is smart to boot!

deg15:03:15

@mtnygard, so, re your tempid answer above. I don't understand.

deg16:03:51

Hmm, are you saying that upsert does not work? and create does not return the id? So, if I want to add a maybe new entity, I need to: - Try to get it - If found, update with whatever changes I have - If not found, create it - After creating it, if I need to do anything else with the entity, either get it again, or have other unique property I can use to identify it

mtnygard16:03:11

@deg You're going to have to start assigning correlation-id's to your posts here... I think we're getting out-of-order and interleaved responses.

mtnygard16:03:10

Let's start from your needs. In your domain, is there a "natural" key for your entities?

deg16:03:41

In most cases, yes; the name of the object. I'm still playing as I code, so I'm not sure that the answer is always yes.

mtnygard16:03:40

OK, when there is a natural key: use the :identity option in the schema. When POSTing such an entity, use the attribute. It will upsert every time based on that attribute.

deg16:03:44

To a large extent, I'm writing this as a learning exercise, so my goal will often be to push the tool to the edge.

mtnygard16:03:30

When other entities need to refer to it, use a lookup ref as described in http://docs.datomic.com/identity.html as the value of the ref attribute that should point to that object.

mtnygard16:03:46

When querying, just use the object name attribute.

mtnygard16:03:03

If you can do that in every case, then you shouldn't need to touch the db/ids.

deg16:03:48

ok. I'm going to dive into my keyboard to reorganize my code this way. Back in a bit if I hit snags. Thanks!! (Feels like I'm commandeered this channel as my private support line!)

mtnygard16:03:16

@deg No worries. Also, I've confirmed that the paragraph you mentioned ("The JSON packet above, because it does not contain a :db/id field refers to a new entity and the return value from the Vase service will return its newly created :db/id. On the other hand, the following JSON refers to an existing entity in the database:") is just plain wrong.

mtnygard16:03:35

But at the same time, there's something hinky about the response body from the transact action.

deg18:03:17

@mtnygard Nice improvements to the your_first_api doc in your changes today! A couple of bits that are still potentially unclear: - One note: Vase tracks which schemas it has already applied - this is hinting at deep concept that won't be obvious to many readers: that the schemas are tuples in the database rather than static definitions. It would be great to explain this, along with why/what, earlier in the doc. - ...treats it as a new entity and attaches a tempid - What is a tempid?

ddeaguiar20:03:10

@deg this is an implementation detail, but Vase uses for tracking schemas (https://github.com/rkneufeld/conformity). This is subject to change, though

ddeaguiar20:03:29

but we should probably capture that in the doc as well