package tc.oc.api.model; import java.util.Collection; import javax.inject.Inject; import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import tc.oc.api.docs.virtual.Model; import tc.oc.api.docs.virtual.PartialModel; import tc.oc.api.http.HttpOption; import tc.oc.api.message.types.PartialModelUpdate; import tc.oc.api.message.types.UpdateMultiRequest; import tc.oc.api.message.types.UpdateMultiResponse; import tc.oc.commons.core.concurrent.FutureUtils; import static com.google.common.base.Preconditions.checkArgument; /** * Generic base class for services that provide CRUD operations on a particular model. * @param <Partial> Base type for all outgoing documents i.e. common ancestor to all of the model's interfaces * @param <Complete> Type of incoming documents, i.e. the "complete" model */ public class HttpModelService<Complete extends Model, Partial extends PartialModel> extends HttpQueryService<Complete> implements ModelService<Complete, Partial> { @Inject private ModelMeta<Complete, Partial> meta; @Override public TypeToken<Partial> partialType() { return meta.partialType(); } protected String updateMultiUri() { return collectionUri("update_multi"); } protected String memberUri(Partial model) { checkArgument(model instanceof Model); return memberUri(((Model) model)._id()); } protected String memberUri(Partial model, String action) { checkArgument(model instanceof Model); return memberUri(((Model) model)._id(), action); } @Override public ListenableFuture<Complete> update(String id, PartialModelUpdate<Partial> request) { return handleUpdate(client().put(memberUri(id), request, meta.completeType(), HttpOption.INFINITE_RETRY)); } @Override public ListenableFuture<Complete> update(String id, Partial partial) { return update(id, updateRequest(partial)); } @Override public ListenableFuture<Complete> update(Partial partial) { if(!(partial instanceof Model)) { throw new IllegalArgumentException("Partial model has no _id field"); } Model model = (Model) partial; if(model._id() == null) { throw new IllegalArgumentException("_id is null"); } return update(model._id(), partial); } @Override public <T extends Partial> ListenableFuture<UpdateMultiResponse> updateMulti(Collection<T> models) { return updateMulti((UpdateMultiRequest<T>) () -> models); } @Override public ListenableFuture<UpdateMultiResponse> updateMulti(UpdateMultiRequest<? extends Partial> request) { if(request.documents().isEmpty()) { return Futures.immediateFuture(UpdateMultiResponse.EMPTY); } else { return client().post(updateMultiUri(), request, UpdateMultiResponse.class, HttpOption.INFINITE_RETRY); } } protected <R extends UpdateMultiResponse, T extends Partial> ListenableFuture<R> updateMulti(Collection<T> models, Class<R> returnType) { return client().post(updateMultiUri(), (UpdateMultiRequest<T>) () -> models, returnType, HttpOption.INFINITE_RETRY); } protected ListenableFuture<Complete> handleUpdate(ListenableFuture<Complete> future) { return FutureUtils.peek(future, this::handleUpdate); } protected void handleUpdate(Complete doc) {} }