prismicio-community / java-kit

Community maintained development kit for Prismic and the Java language
https://prismic.io
15 stars 38 forks source link

Api.get(String url) throws exception #75

Closed Addibro closed 4 years ago

Addibro commented 4 years ago

Hello!

Using prismic v. 2.0.0 in our spring boot application.

Using Api.get(String url) method to instantiate my api gets me an IllegalArgumentException: argument "in" is null.

I trace the error from your HttpClient.fetch() method, catching a IOException applying errorTest of "Unknown error". But it is in fact ObjectMapper().readTree() that throws the exception, throwing a throw new IllegalArgumentException(String.format("argument \"%s\" is null", paramName));

Please help!

Thank you.

bgooren commented 4 years ago

Since last night we are seeing the same issue: in HttpClient, when catching IOException (the inner catch), the code assumes that there is always a non-null httpConnection.getErrorStream(), which is (obviously) false. When a connection error occurs in the middle of downloading a valid response, there will not be an error stream.

It looks like the API is a bit flaky for us in the last 24h as we started getting random IOExceptions. Unfortunately it looks like prismic is no longer supporting this library, as the code has not been touched for over 2 years. Since we use it extensively I am going to fork it and apply some small fixes, I don't feel like waiting any longer or implementing hacky catch clauses in our own code.

Addibro commented 4 years ago

Good catch! I'm in contact with their support right now asking about maintenance of this lib to see if they plan to do anything about it.

After adding a proxy to the Api instantiation the request is successful, although it does not return newly added documents added in Prismic dashboard. I have to reboot my spring boot app to get the new documents. Have you experienced this behaviour?

bgooren commented 4 years ago

That's normal behaviour: there is a cache to prevent making two round trips for each content element (1 - get current repository reference, 2 - execute your query).

You can either disable the cache (look for the overloaded constructors on the Api class that take a cache parameter), or implement a basic webhook endpoint. We did the latter and simple clear the cache when anything in the prismic repository changes :-)

Addibro commented 4 years ago

Aight! Yep, I use that exact overloaded constructor using NoCache for the cache parameter. You mean you created a webhook in Prismic and cleared your cache how exactly? And which cache are you referring to?

bgooren commented 4 years ago

Hi! I wrote a custom cache that wraps the Prismic API, which caches (transformed) business results. I clear this custom cache when we receive a webhook call.

Without any cache you should see results right away, so my guess is you are reusing your Api object/instance... Is that the case?

Every prismic repository has a master reference (let's say it's a sort of version string), which is updated whenever changes to content are published. When you create an Api instance, it will fetch the current master reference and cache it for 5 seconds (or get it from cache if already in there). This master reference is used in all queries; So when you reuse an Api instance you are "stuck" on the master reference fetched on creation.

Normally you should create a new Api instance for each user (http) request; Since it caches the master reference for up to 5 seconds, it's a really light operation. Queries are cached according to a ttl returned by prismic - but that's not that relevant: when the master reference changes, all queries will show the new content because they include the master reference in their url (so the url changes too).

You should also provide the cache to newly created Api instances explicitly, to make caching work across (user) requests. I provide a (global, singleton)new Cache.BuiltInCache( 2500 ) to the Api constructor.

The reason I have implemented a webhook listener is that this allows me to refresh the abovementioned cache (e.g. replace it, to prevent waiting up to 5 seconds before changes are visible due to master ref caching), and clear our custom cache which wraps prismic.

If you have any more questions, let me know :-)

bgooren commented 4 years ago

PS See https://user-guides.prismic.io/en/articles/2495658-changes-in-prismic-not-appearing-on-your-website

Addibro commented 4 years ago

This is way better support than I get from Prismic 😄 Thank you. I'll fiddly around with Api.get() operation and restructure it to be called on every http request. But a webhook would be even better. Then rather than calling Api.get() on every user request, you would do Api.get() whenever a webhook is triggered.

bgooren commented 4 years ago

Sure thing 👍🏻

It took me a while to understand how things are set up in prismic - happy to hear I could pass it on.

Yeah, you can also have a single Api instance which you refresh when you receive a webhook call.

Addibro commented 4 years ago

Yes, thanks a bunch!

By refresh, you mean re-instantiate with Api.get()?

bgooren commented 4 years ago

Yes - create a new instance. Since you are doing this when the master ref changes, there is also no added value to sharing a cache across instances. I do recommend using caching though, since you can be certain that nothing will change (so: don't provide a NoCache instance).

Addibro commented 4 years ago

Yep, I wrap everything in a singleton which gives me access only to the Api, which is updates on every request right now. I added a BuiltInCache now as well. Seems to work so far!

Addibro commented 4 years ago

Thanks @bgooren !