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

Refactoring `User´ #268

Closed
wants to merge 9 commits into from
Closed

Refactoring `User´ #268

wants to merge 9 commits into from

Conversation

d4rken
Copy link
Contributor

@d4rken d4rken commented Mar 10, 2018

  • Introduced builder pattern for User, User is now immutable.
  • Reduced duplicate/simplified code related to User and moved User related implementation details from Client to `User´
  • Removed methods that were just used internally and migrated them to Client.setUser(user)
  • Tagged/deprecated all public APIs that set specific User fields. In the next semvar major release these could be removed.
  • Added unit tests for the new code and adapted the existing tests such that both the new and the now deprecated methods are covered.
  1. Cleaner API after the next major release removes the deprecated methods
  2. Easier future SDK updates. Want to add new User information? Just update User.
  3. User is now exposed via Client.getUser() which solves my issue in Access user-id? #265

@fractalwrench

@d4rken d4rken mentioned this pull request Mar 10, 2018
Those are some pedantic rule sets...
I think single line IFs are A OKAY as are >100LL in 2018...
😞
@coveralls
Copy link

coveralls commented Mar 10, 2018

Pull Request Test Coverage Report for Build 1190

  • 55 of 70 (78.57%) changed or added relevant lines in 5 files are covered.
  • 19 unchanged lines in 4 files lost coverage.
  • Overall coverage decreased (-0.4%) to 70.341%

Changes Missing Coverage Covered Lines Changed/Added Lines %
sdk/src/main/java/com/bugsnag/android/NativeInterface.java 0 3 0.0%
sdk/src/main/java/com/bugsnag/android/Error.java 0 4 0.0%
sdk/src/main/java/com/bugsnag/android/Client.java 12 16 75.0%
sdk/src/main/java/com/bugsnag/android/Bugsnag.java 0 4 0.0%
Files with Coverage Reduction New Missed Lines %
sdk/src/main/java/com/bugsnag/android/NativeInterface.java 1 6.76%
sdk/src/main/java/com/bugsnag/android/Bugsnag.java 2 10.17%
sdk/src/main/java/com/bugsnag/android/Client.java 3 48.09%
sdk/src/main/java/com/bugsnag/android/SessionTracker.java 13 54.62%
Totals Coverage Status
Change from base Build 1186: -0.4%
Covered Lines: 1755
Relevant Lines: 2495

💛 - Coveralls

@d4rken
Copy link
Contributor Author

d4rken commented Mar 11, 2018

  • testMultipleSessionFiles passes locally, is this a flaky test?

@fractalwrench fractalwrench self-assigned this Mar 12, 2018
@fractalwrench
Copy link
Contributor

Yep, it's a bit of a flaky test on Travis.

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.io.IOException;

/**
* Information about the current user of your application.
*/
class User implements JsonStream.Streamable {
Copy link
Contributor

Choose a reason for hiding this comment

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

This class would need to be public if exposed as part of the API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course!

private String name;
@Nullable private String id;
@Nullable private String email;
@Nullable private String name;
Copy link
Contributor

Choose a reason for hiding this comment

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

These fields can now be final

User() {
}

User(String id, String email, String name) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think for now it's worth keeping this as a public convenience constructor that builds a Builder.

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 disagree. This was never public, so why would we want to introduce something new, "for now", that would be deprecated along with the other APIs?

Exposing only the builder to create User objects is what will make it easier to add additional user fields in the future, while keeping the API clean.


User(@NonNull User user) {
this(user.id, user.email, user.name);
User(Builder builder) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be private

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making this private will generate a synthetic accessor call due to access from User.Builder.build() as there is no advantage to making private I'd prefer to avoid increasing the method count unnecessarily by one (once deprecated methods are removed, bugsnags method count be reduced even more which is always nice for a library).

*/
public static Builder builder(User user) {
return new Builder(user);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can get rid of the two methods above, and favour one way of creating a builder:

User user = new User.Builder().id("Foo").build();

The second method could be replaced by something like the following:

User.Builder builder = user.toBuilder();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, this would also simplify internal use as user.toBuilder()... is nicer than User.builder(user)....

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 will also drop the User.builder() method too to save another method as the builder constructors are public anyways.

public static class Builder {
@Nullable String id;
@Nullable String email;
@Nullable String name;
Copy link
Contributor

Choose a reason for hiding this comment

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

Fields can be private

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As there is no advantage to making them private, we should keep them package protected as this would save another 3 methods (synthetic accessors).

public Builder(User user) {
id(user.getId());
email(user.getEmail());
name(user.getName());
Copy link
Contributor

Choose a reason for hiding this comment

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

A test case for null users would be useful here

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 agree, also found a mistake in Error that could lead to NPE, fixing that now.

.remove(USER_NAME_KEY)
.apply();
notifyBugsnagObservers(NotifyType.USER);
public User getUser() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to add @see #setUser(User) to the JavaDoc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

*/
public void setUserId(String id) {
setUserId(id, true);
public void setUser(@Nullable User user) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The JavaDocs for this method should include as much information as before, although we'll need to change how the params are handled:

Set details of the user currently using your application.
-     * You can search for this information in your Bugsnag dashboard.
-     * <p/>
-     * For example:
-     * <p/>
-     * client.setUser("12345", "james@example.com", "James Smith");
-     *
-     * @param id    a unique identifier of the current user (defaults to a unique id)
-     * @param email the email address of the current user
-     * @param name  the name of the current user

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That information is available in the javadoc of User though? Which the dev will see when they construct a User object. By duplicating java doc here, everytime User is changed, the java doc has to be updated in 3 places instead of just in User where it belongs.

* @param id a unique identifier of the current user (defaults to a unique id)
* @param email the email address of the current user
* @param name the name of the current user
* @param user The user information to set or NULL to reset to defaults.
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs to include same information as before about id/email/name


@Test
public void testRepo() {
SharedPreferences sharedPref = getSharedPrefs(InstrumentationRegistry.getContext());
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be worth sharedPref.edit().clear().commit() beforehand here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely, to guard against any future changes that could make this test flaky.

}

@Test
public void testBuilder_values() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Our codestyle uses camelcase, looks like I've got a bit of work to do configuring our checkstyle as it should have caught this D=

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have you considered changing it? I think
testMethodA_defaults and testMethodA_nullArgument
is more readable than
testMethodADefaults and testMethodANullArguments
because it's immediately clear that it's two tests for the same method, just differents aspects.

I'll change it though, I don't have strong feelings about this.

@fractalwrench
Copy link
Contributor

@d4rken left a few minor comments, mostly this looks very good and I'm quite excited to get it merged :)

@kattrali will also have a look through the PR, but feel free to ping me in the meantime if you have any questions/clarifications

The builders argument can now also be null.
Fixed unit tests method name style and added additional tests.
Check that preferences are empty before testing.
@d4rken
Copy link
Contributor Author

d4rken commented Mar 21, 2018

What's the status on this?

@fractalwrench
Copy link
Contributor

@kattrali is planning to review this with a second pair of eyes, but is currently at a conference for the rest of the week.

@anastasia-zaitsewa
Copy link

Would like to see this merged as well ➕

@d4rken
Copy link
Contributor Author

d4rken commented Apr 22, 2018

Is there no interest in merging this?

@d4rken
Copy link
Contributor Author

d4rken commented May 31, 2018

@fractalwrench @kattrali What's the issue here?

@d4rken
Copy link
Contributor Author

d4rken commented Jul 30, 2018

@fractalwrench I'm annoyed that you encouraged a PR for this and now it just rots away 👎. Why did I waste my time on this?

@fractalwrench
Copy link
Contributor

@d4rken I'm really sorry about how this was handled - it doesn't live up to our standards and I can only apologise that it happened. Shortly after this PR was opened, our policy on reviewing external PRs changed so that no longer accept changesets with feature additions or refactors of existing functionality. Coupled with an automation change that syncs GitHub issues to an internal ticketing system, I'm sorry to say that this PR slipped through the cracks.

As a consequence of this, we will:

  • Update the Contributing guidelines across all our repositories to make the difference between bug fix/feature PRs much clearer.
  • Create an internal ticket for providing accessors on every field within the Report payload.
  • Raise this at our next engineering retrospective meeting, to avoid this happening again to others in future.

Again, I'd like to say how sorry we are - I've been in this position and know there's nothing more frustrating than spend time opening a PR and having it hang around for months. If there are any additional actions on our part that you think might be helpful in making this right, please let us know.

@d4rken
Copy link
Contributor Author

d4rken commented Jul 30, 2018

😦

Shortly after this PR was opened, our policy on reviewing external PRs changed so that no longer accept changesets with feature additions or refactors of existing functionality.

What caused this change?

As a consequence of this, we will:

  • Update the Contributing guidelines across all our repositories to make the difference between bug fix/feature PRs much clearer.
  • Create an internal ticket for providing accessors on every field within the Report payload.
  • Raise this at our next engineering retrospective meeting, to avoid this happening again to others in future.

👍

If there are any additional actions on our part that you think might be helpful in making this right, please let us know.

Don't worry, I'm still a happy customer.

@kattrali
Copy link
Contributor

What caused this change?

There's a few causes I can chime in on:

  1. We have an internal roadmap which may be in conflict with features developed externally, which is why it often faster to send feedback about parts of the library or Bugsnag interface which are difficult to use or introduce friction to your workflow. We can pull together everyone's feedback and think about how all of these pieces should work together.
  2. Generally, pull requests which include significant code changes are difficult to review. Error reporters have to work in crashy, multi-threaded environments and overlooking small adjustments can mean failure to capture reports, deadlocking when calling a function from a background thread unexpectedly, or worse. Features and refactors developed internally go through a design documentation and review process prior to code being written. Its not a burden we want to put on our customers as the process may sometimes seem opaque.
  3. While we want each Bugsnag library to be an example of idiomatic use of both language and the development environment, we also want terminology and core concepts to be the same across different Bugsnag libraries, which makes integration easier for customers using multiple platforms and providing support easier for teams at Bugsnag.

All that being said, we absolutely welcome feedback about using Bugsnag via either GitHub issues or support@bugsnag.com. Thanks again.

lemnik pushed a commit that referenced this pull request Jun 2, 2021
Specify millisecond timeout correctly for entire request
rich-bugsnag pushed a commit that referenced this pull request Sep 3, 2021
test: support different .ipa outputs in MR build
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants