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

Vsp over object v3 mc #245

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open

Vsp over object v3 mc #245

wants to merge 21 commits into from

Conversation

marcin-cebo
Copy link
Contributor

1.No sure if we want to put deprecation messages on ObjectV2 method just now because ObjectV3 REST API doesn't seem to be in final shape.
2.I realised that ObjectV3 Rest API doesn't return updated and eTag fields.

Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Under the hood ObjectV3 endpoint is used.
This is initial shape, and we are expecting Payloads to change in the future.
Copy link
Contributor

@kleewho kleewho left a comment

Choose a reason for hiding this comment

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

Some of the comments are applicable to more places


import java.util.Map;

public abstract class CreateUser extends UserEndpoint<CreateUser, EntityEnvelope<User>, CreateUserResult> implements CustomIncludeAware<CreateUser> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be an interface. It'll save us few lines. This requires few additional changes in generic parameter boundaries from Endpoint to RemoteAction. This how the full interface would look like:

public interface CreateUser extends RemoteAction<CreateUserResult>, CustomIncludeAware<CreateUser> {
    CreateUser name(String name);

    CreateUser email(String email);

    CreateUser profileUrl(String profileUrl);

    CreateUser externalId(String externalId);

    CreateUser custom(Map<String, Object> custom);

    CreateUser status(String status);

    CreateUser type(String type);

    CreateUser userId(UserId userId);

    static CreateUser create(
            final PubNub pubNub,
            final TelemetryManager telemetryManager,
            final RetrofitManager retrofitManager,
            final TokenManager tokenManager) {
        final CompositeParameterEnricher compositeParameterEnricher = CompositeParameterEnricher.createDefault();
        return new CreateUserCommand(pubNub, telemetryManager, retrofitManager, tokenManager, compositeParameterEnricher);
    }
}

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 contact you to talk about this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. Let's talk

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 tried to introduce interface but is seems more easier approach to reduce code we can get rid of CreateUserCommand class and move all login from that class to CreateUser that will be changed from abstract to non abstract class. I also removed UserEndoint class.

private String externalId;
private Map<String, Object> custom;
private String status;
private String type;
Copy link
Contributor

Choose a reason for hiding this comment

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

All those fields could have generated fluent setters with lombok, just like we have for most of other implementations. It'll save us few lines. It'll look like this:

@Accessors(chain = true, fluent = true)
public final class CreateUserCommand extends CreateUser implements HavingCustomInclude<CreateUser> {
    @Setter
    private String name;

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 can change it without big enthusiasm. With this construction code is more difficult to read for people that don't know lombok. But this is good chance for me to learn it :)
FYI
In SetChannelMetadataCommand and SetUUIDMetadataCommand this lombok is not used.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not a fan of lombok either, but if we have it already and it's used in quite a few places we can as well just make use of it

Copy link
Contributor

Choose a reason for hiding this comment

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

In SetChannelMetadataCommand and SetUUIDMetadataCommand this lombok is not used.

That might have been an oversight. AFAIR it was a big big PR as well, so I might have missed something

import java.util.HashMap;
import java.util.Map;

public final class CreateUserCommand extends CreateUser implements HavingCustomInclude<CreateUser> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an implementation so I'd make it package private. There should be no need for customer to use these

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 like this idea.

public SetChannelMetadata.Builder setChannelMetadata() {
return SetChannelMetadata.builder(this, this.telemetryManager, this.retrofitManager, this.tokenManager);
}

public CreateSpace createSpace() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would likely see here all required parameters instead of everything being optional. That would mean that all space related methods should have spaceId at least. This could bring additional changes in other places: making it required in the *SpaceCommand and also most probably the SpaceEndpoint will no longer be necessary

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 like this idea.

Copy link
Contributor

Choose a reason for hiding this comment

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

I knew that you'll like it :)

protected UserId effectiveUserId() {
try {
return (userId != null) ? userId : getPubnub().getConfiguration().getUserId();
} catch (PubNubException e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seeing this catch I think we made a mistake by keeping uuid in configuration as a base field instead of UserId. We wouldn't have to have this here for simple getUserId

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok


@NoArgsConstructor
public class FetchSpaceResult extends SpaceResult {
public FetchSpaceResult(EntityEnvelope<Space> entityEnvelope) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess you missed final

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks.

public interface SpaceService {

@POST("/v3/objects/{subKey}/spaces/{spaceId}")
@Headers("Content-Type: application/json; charset=UTF-8")
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we include it everywhere where we have Body or not include it at all?

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 delete it.
I found @headers in ChannelMetadataService and UUIDMetadataService so I will delete them from there as well.

@Getter
@ToString
@AllArgsConstructor
public abstract class UserPayload {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd use just @Data instead of these annotations

Copy link
Contributor Author

@marcin-cebo marcin-cebo Sep 8, 2022

Choose a reason for hiding this comment

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

@Data introduces @Setter. I am wondering if we want to have setters for those fields?

Copy link
Contributor

Choose a reason for hiding this comment

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

it doesn't if you have everything final

@Getter
@ToString
@AllArgsConstructor
public abstract class SpacePayload {
Copy link
Contributor

Choose a reason for hiding this comment

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

@Data should be all right here instead of currently present

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I will change it.


import java.util.Map;

public abstract class CreateSpace extends SpaceEndpoint<CreateSpace, EntityEnvelope<Space>, CreateSpaceResult> implements CustomIncludeAware<CreateSpace> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the return value is CreateSpaceResult and not just Space? In Kotlin we return just Space. Could you please align the return values for all new methods to what we have in Kotlin already?

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 forgot that in Kotlin we get rid of status in response. I will change CreateSpaceResult to Space. Status might be inferred by Space object being present or exception being thrown
e.g. when creating space with id that already exists following exception is thrown:
{"error":{"message":"Requested resource already exists.","source":"metadata"},"status":409}

Copy link
Contributor

@kleewho kleewho left a comment

Choose a reason for hiding this comment

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

The ObjectsV2 implementation is one of the most complicated things in the JavaSDK and I think we went over the top with this. I don't think it needs to be as complicated. I'm not sure what you think about it, but feel free to drop all the complicated bits of that implementation if you share this impression

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class Space extends PNObject {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we have a SpaceId as a type for id field for this class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Good catch. I will create class similar to PNObject but with no id and add:
private SpaceId id;
to Space.java.
I will also add custom interceptor to map id as String in REST response to SpaceId .

public class SpaceIdDeserializer implements JsonDeserializer {
@SneakyThrows
@OverRide
public SpaceId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String spaceIdValue = json.getAsString();
return new SpaceId(spaceIdValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class User extends PNObject {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same story as with Space. I think its id should have type UserId

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 add it. Thanks.

@Setter(AccessLevel.PACKAGE)
@NoArgsConstructor
@ToString(callSuper = true)
public class RemoveSpaceResult extends EntityEnvelope<JsonElement> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Simpler alternative

@Data
public class RemoveSpaceResult {
    private final int status;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. I will use magic of lombok.

}

@Override
public PNObject setCustom(Object custom) {
Copy link
Contributor

@kleewho kleewho Sep 9, 2022

Choose a reason for hiding this comment

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

I think custom should not be an Object but rather should have the same type as we expect when we createSpace, so Map<String, Object>.

What would you say about this simpler version of Space?

@Data
public class Space {
    @NonNull
    private final SpaceId id;
    private final String name;
    private final String description;
    private final String status;
    private final String type;
    private final Map<String, Object> custom;
}

probably we'd need custom deserializer for SpaceId

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 this is good improvement. I guess we need to add eTag and updated field so:

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@DaTa
public class Space extends ObjectVsp {

@EqualsAndHashCode.Include
@JsonAdapter(SpaceIdDeserializer.class)
@Setter
private SpaceId id;
private String name;
private String description;
private String status;
private String type;

@Override
public Space setCustom(Map<String, Object> custom) {
    super.setCustom(custom);
    return this;
}

}

@DaTa
@accessors(chain = true)
public class ObjectVsp {

@JsonAdapter(CustomPayloadJsonInterceptor.class)
@Setter
protected Map<String, Object> custom;

protected String updated;
protected String eTag;

}

if (input.body() != null) {
return input.body().getData();
} else {
return new Space();
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 it should rather be an error. We are expecting body in case of successful response

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you suggest? Something like this ?
if (input.body() != null) {
return input.body().getData();
} else {
throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build();
}
In case of error program will not hit this method. So return new Space() should never happen.

I was looking at PNSetChannelMetadataResult, GetChannelMetadataCommand and there empty object creation but maybe it's more precise to throw exception.

Removed *Command classes
Removed UserEndpoint classes
Made CreateSpace, FetchSpace..., CreateUser, FetchUser... non abstract
Introduced interfaces for all User/Space operations
e.g. CreateUser, CreateSpace
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.

2 participants