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

Interceptors api #771

Merged
merged 12 commits into from
Jul 6, 2017
Merged

Conversation

rsinukov
Copy link
Contributor

Hi.
There is a proof of concept for interceptors API and sample LoggingInterceptor to see how it works.
Main idea of this PR is to create Interceptor.Chain in every PreparedOperation#executeAsBlocking() method and put in chain RealInterceptor, which will do real job. So, all code in RealInterceptor#intercept() is copypaste of past executeAsBlocking() code.

@rsinukov
Copy link
Contributor Author

closes #542 and part of #541

@codecov-io
Copy link

codecov-io commented Mar 18, 2017

Codecov Report

Merging #771 into master will increase coverage by 0.14%.
The diff coverage is 95.09%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #771      +/-   ##
==========================================
+ Coverage   96.85%   96.99%   +0.14%     
==========================================
  Files          87       89       +2     
  Lines        2636     2727      +91     
  Branches      296      302       +6     
==========================================
+ Hits         2553     2645      +92     
  Misses         50       50              
+ Partials       33       32       -1
Impacted Files Coverage Δ
.../com/pushtorefresh/storio/sqlite/StorIOSQLite.java 100% <ø> (ø) ⬆️
...ntentresolver/operations/internal/RxJavaUtils.java 100% <ø> (ø) ⬆️
...com/pushtorefresh/storio/sqlite/queries/Query.java 100% <ø> (ø) ⬆️
...tentresolver/operations/delete/PreparedDelete.java 100% <ø> (ø) ⬆️
...storio/sqlite/operations/internal/RxJavaUtils.java 100% <ø> (ø) ⬆️
.../pushtorefresh/storio/sqlite/queries/RawQuery.java 100% <ø> (ø) ⬆️
...io/contentresolver/operations/put/PreparedPut.java 100% <ø> (ø) ⬆️
...torio/sqlite/operations/delete/PreparedDelete.java 100% <100%> (ø) ⬆️
...om/pushtorefresh/storio/sqlite/impl/ChainImpl.java 100% <100%> (ø)
...ions/delete/PreparedDeleteCollectionOfObjects.java 97.72% <100%> (+0.05%) ⬆️
... and 28 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 13a5386...26b4dfe. Read the comment docs.

@nikitin-da
Copy link
Collaborator

And closes #79 =)

Copy link
Collaborator

@nikitin-da nikitin-da left a comment

Choose a reason for hiding this comment

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

Awesome work man! 👍 Example of usage looks great!

settings.gradle Outdated
@@ -1,4 +1,4 @@
include ':storio-common'
include ':storio-common', ':logginginterceptor'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe move to the new line corresponding to other modules declaration?

@NonNull
private final Logger logger;

public LoggingInterceptor(@NonNull Logger logger) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suppose make it private and create via static method. To make it easer to support compatibility in the future)

interceptors.addAll(registeredInterceptors);
interceptors.add(realInterceptor);

return new ChainImpl(interceptors, 0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe wrap it with unmodifiable container?

@NonNull
private final List<Interceptor> interceptors;

@NonNull
Copy link
Collaborator

Choose a reason for hiding this comment

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

excess annotation)

@Nullable // can be null on PreparedGetObject
@Override
public <Result> Result proceed(@NonNull PreparedOperation<Result> operation) {
if (index >= interceptors.size()) throw new AssertionError();
Copy link
Collaborator

Choose a reason for hiding this comment

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

IllegalStateException with some message?

}

@NonNull
protected abstract Interceptor getRealInterceptor();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe getRealCallInterceptor or something like this?

}

private void checkInterceptorsCalls() {
assertThat(callCount.get()).isEqualTo(2);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add inOrder verification?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Strange, in my machine tests fail when I mock Interceptor with some internal Mockito error. Can you check it on your machine pls?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, I should update robolectric to support mockito2 (robolectric/robolectric#2677). I will done it soon.

@@ -177,6 +178,9 @@
@NonNull
public abstract LowLevel lowLevel();

@NonNull
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems we will need javadoc for it and for interceptor and chain too=)

Copy link
Collaborator

@geralt-encore geralt-encore left a comment

Choose a reason for hiding this comment

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

Good stuff!

@NonNull
private final int index;

private int calls;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be changed to just a boolean flag since according to the code we don't really care about actual number of calls.

Copy link
Contributor Author

@rsinukov rsinukov Mar 26, 2017

Choose a reason for hiding this comment

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

Actually we check that it wasn't called more than once

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 for boolean)
And should it be thread safe?

}

// Call the nextChain nextInterceptor in the chain.
final ChainImpl nextChain = new ChainImpl(interceptors, index + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about passing an Iterator so we can get rid of the index?

final Result result = nextInterceptor.intercept(operation, nextChain);

// Confirm that the nextChain nextInterceptor made its required call to chain.proceed().
if (index + 1 < interceptors.size() && nextChain.calls != 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Won't the same checks be performed inside proceed of nextChain?

@NonNull
@Override
protected Interceptor getRealInterceptor() {
return new RealInterceptor();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be just an anonymous class (and below)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that standalone class is more readable. @nikitin-da what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

A agree that variant with standalone class will be more readable.
Also if we will decided some days to make it public, there will be less things to change=)

@@ -62,4 +62,7 @@
@NonNull
@CheckResult
Single<Result> asRxSingle();

@NonNull
Object getData();
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about providing some common interface or wrapper to avoid passing plane object?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With interface it would be impossible to use third side classes.
We can add generic param to all prepared operation, if you think that it worth doing.

Copy link
Member

Choose a reason for hiding this comment

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

I think we need to try generics in prepared operations, Object seems too weird…

Copy link
Contributor Author

@rsinukov rsinukov Mar 29, 2017

Choose a reason for hiding this comment

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

Generics impossible for PreparedGetOperation, cause it can have either Query or RawQuery. We can create common interface for them, but what a point? Anyway, it will not add any details, cause user will see common generic parameter inside Interceptor which is same as seeing Object.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, then you deal with all complaints from users :D

Copy link
Member

@artem-zinnatullin artem-zinnatullin left a comment

Choose a reason for hiding this comment

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

JESOS, this is huge, but looks good! Minor comments

@@ -62,4 +62,7 @@
@NonNull
@CheckResult
Single<Result> asRxSingle();

@NonNull
Object getData();
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to try generics in prepared operations, Object seems too weird…

<Result> Result intercept(@NonNull PreparedOperation<Result> operation, @NonNull Chain chain);

/**
* Encapsulates logic of getting from one interceptor to the next one.
Copy link
Member

Choose a reason for hiding this comment

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

of proceeding from one interceptor to another

@Nullable
@Override
public <Result> Result intercept(@NonNull PreparedOperation<Result> operation, @NonNull Chain chain) {
final long startMillis = System.currentTimeMillis();
Copy link
Member

Choose a reason for hiding this comment

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

SystemClock.elapsedRealtime()


final Result result = chain.proceed(operation);

final long endMillis = System.currentTimeMillis();
Copy link
Member

Choose a reason for hiding this comment

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

SystemClock.elapsedRealtime()

@rsinukov
Copy link
Contributor Author

rsinukov commented Apr 9, 2017

@artem-zinnatullin we with @nikitin-da decided to add generic param to PreparedOperation

@@ -28,6 +29,28 @@
public class PreparedGetCursorTest {

@Test
public void shouldThrowInNoQueryOrRawQueryIsSet() {
Copy link
Member

Choose a reason for hiding this comment

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

if

@@ -62,4 +62,7 @@
@NonNull
@CheckResult
Single<Result> asRxSingle();

@NonNull
Data getData();
Copy link
Member

Choose a reason for hiding this comment

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

So basically this does not help interceptors to be type-safe but gives a way to get data without need in casting PreparedOperation to some particular implementation.

I guess we'll have to ship it as 2.x then since it's addition to an interface which is a breaking change (when I was giving greenlight to that I was thinking just about , Data> generic which is not breaking but forgot that it'll add a method)

@artem-zinnatullin artem-zinnatullin merged commit 9907616 into pushtorefresh:master Jul 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants