Firestore's WriteBatch.update() won't take angularfirestore2's AngularFirestoreDocument - angularfire2

I'm trying to do a batch write using Firestore's WriteBatch. I'm trying to add updates to the WriteBatch and that requires a DocumentReference. I have a AngularFirestoreDocument and can't figure out how to cast that to a DocumentReference and get it to compile or run correctly.
I've tried to cast it to any,unknown, and DocumentReference and use the AngularFirestoreDocument as is.
export class FinancialService {
private transCollection: AngularFirestoreCollection<Transaction>;
batch_update_trans(trans) {
let writer = this.afs.firestore.batch();
this.transCollection = this.afs.collection<Transaction>('balance_sheets/'+this.balance_sheet_id+"/transactions/");
for (let t of trans) {
writer.update(this.transCollection.doc(id),t);
}
return writer.commit();
}
}
if I include the AngularFirestoreDocument as is like above I get this compile time error:
error TS2345: Argument of type 'AngularFirestoreDocument<{}>' is not assignable to parameter of type 'DocumentReference'.
Type 'AngularFirestoreDocument<{}>' is missing the following properties from type 'DocumentReference': id, firestore, parent, path, and 2 more.
If I cast the AngularFirestoreDocument to a DocumentReference:
writer.update(<DocumentReference>this.transCollection.doc(id),t);
I get this compile time error:
error TS2352: Conversion of type 'AngularFirestoreDocument<{}>' to type 'DocumentReference' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Type 'AngularFirestoreDocument<{}>' is missing the following properties from type 'DocumentReference': id, firestore, parent, path, and 2 more
If I cast the AngularFirestoreDocument to a DocumentReference to unknown:
writer.update(<unknown> this.transCollection.doc(id),t);
It fails to compile with this error:
error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'DocumentReference'.
If I cast the AngularFirestoreDocument to a DocumentReference to any:
writer.update(<any> this.transCollection.doc(id),t);
it does compile, but I get a runtime error when I try go batch update docs this way:
Uncaught (in promise): FirebaseError: [code=invalid-argument]: Function WriteBatch.update() requires its first argument to be a DocumentReference, but it was: an object
FirebaseError: Function WriteBatch.update() requires its first argument to be a DocumentReference, but it was: an object
I was expecting the code to compile and successfully batch update Transaction docs.

OK, I figured out how to do this using this really helpful article that provides a lot of documentation that angularfire2 doesn't have itself:
Firestore Advanced Usage : angularfire - atomic writes
You can access the javascript API through angularfire2's firestore module and create a DocumentRef through that API, instead of using angularfire2's AngularFirestoreDocument type.
The working code looks like this:
export class FinancialService {
batch_update_trans(trans) {
let writer = this.afs.firestore.batch();
let transDoc = null;
for (let t of trans) {
transDoc = this.afs.firestore.doc(
'balance_sheets/'+this.balance_sheet_id+"/transactions/"+t.id);
writer.update(transDoc,t);
}
return writer.commit();
}
}

Related

Unable to work with interface imported from another pkg, says method is missing but its there

I'm unable to work with an interface imported from another package and not sure what I should do. Example code:
var inner types.TxData // <-- interface imported from types package
inner = &InternalTx{
ChainId: (*big.Int)(dec.ChainID),
Data: *dec.Data,
}
Out of all the methods listed in the interface, 1 method is not accepted:
func (tx *InternalTx) accessList() types.AccessList { return nil }
Go complains that InternalTx does not implement accessList() to satisfy types.TxData interface, but if i capitalise accessList() to Accesslist() then I get another complaint stating that :
have AccessList() types.AccessList
want accessList() types.AccessList
So i'm very confused what I need to do here?
Edit:
I've implemented also the following based on recent suggestion:
type TxData struct {
types.TxData
}
var inner TxData
internalTx := &InternalTx{
ChainId: (*big.Int)(dec.ChainID),
Data: *dec.Data,
}
inner = TxData{TxData: internalTx}
Issue still persists.
If an interface is declared with an unexported method (such as accessList), then you cannot implement that interface in another package, because that name is not exported. This is a common way to force you to use implementations in the original package.
One way to deal with this is to embed that interface, and then delegate functionality to an implementation of it:
type TxData struct {
types.TxData
}
someImplementation:=// Get an implementation of types.TxData
inner:=TxData{TxData: someImplementation}
This will use the accessList of someImplementation.

Generic parameter 'Element' could not be inferred?

"Generic parameter 'Element' could not be inferred" - this error comes as I write guard statement - what is inside guard statement which causes error that element could not be inferred.
static func makeTokenForCard(with cardinfo: CardInfo) -> Single<String> {
return Single.create {
single in guard let ck = try CheckoutKit.getInstance("pk_123456789876543234567, env: Environment.SANDBOX, debug: true)
else {
let descr = "Unexpectedly Checkout got invalid private key. You may need to update the app."
single(.error(NSError.recreate(error: CheckoutError.invalidPK as NSError, description: descr)))
return
}
single(.success("123456"))
return Disposables.create()
}
}
When I remove this Guard statement - it returns simple String and errors in Single.
Edit :
After getting more into error, I found that its due to throws.
open class func getInstance(_ pk: String, env: Environment, debug: Bool) throws -> CheckoutKit? {
In Simple Guard & wrapping its fine.
So, How to call a method in Single when it has throws some expected error
?
This isn't RxSwift related but more Swift and handling errors.
The simplest answer to your question is to use try? instead try. This will fix the issue.
You can read more about error handling here https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html

Passing RealmDB Results Object as Swift 2 Function Parameter

I am working on a mobile app function using Swift 2 where I am trying to pass a RealmDB Results object as a parameter.
func myFunction(myDBresults: Results){
Code to do stuff
}
Xcode give the error with a little upward arrow pointing at the R in "Results":
Reference to generic type 'Results' requires arguments in <...>
I don't know what is being required here as I still learning about Swift. Could I get some pointers on what is being required here?
Results is a generic type, so you have to either make the function generic or specify type of the object stored in the Results:
func myFunction(myDBresults: Results<MyObjectClass>) {
// do stuff
}
Or, if you wish to have a function which can accept Results of any type:
func myFunction<T: Object>(myDBresults: Results<T>) {
// do stuff
}

Lagom framework / streamed response / websocket / pathCall / Descriptor / Creator instead of Function

I have my service declared this way:
public interface BlogQueryService extends Service {
public ServiceCall<String, Source<String, ?>> tick(int interval);
public ServiceCall<String, Source<String, ?>> tock();
public ServiceCall<NotUsed, Source<PostSummary, ?>> newPosts();
public ServiceCall<String, Source<PostSummary, ?>> getPostSummaries();
#Override
default Descriptor descriptor() {
return named("blog-query").with(
//pathCall("/api/bloggie/tick/:interval", this::tick),
pathCall("/api/bloggie/tock", tock())
//pathCall("/api/bloggie/newPosts", this::newPosts),
//pathCall("/api/bloggie/postSummaries", this::getPostSummaries)
).withAutoAcl(true);
}
}
The tick works. The tock doesn't.
When I invoke it using websocket client (to ws://localhost:9000/api/bloggie/tock ) , I got "undefined" as response, indicating no mapping found for that URL.
After some experimentings, found out why: tick works because it has url param (the :interval). Tick doesn't work because it doesn't have url param. Seriously pathCall requires you to have param in your URL? So I checked the API of Service: http://www.lagomframework.com/documentation/1.0.x/api/java/com/lightbend/lagom/javadsl/api/Service.html
There are several overloaded declarations of pathCall. Apparently the tick uses this one:
static <Request,Response,A> Descriptor.Call<Request,Response> pathCall(String pathPattern, akka.japi.function.Function<A,ServiceCall<Request,Response>> methodRef)
So from the signature, yes it requires the method to take a parameter. So, if the method (such is tock) doesn't take a param, the binding will fail at runtime. So I guess I need to use this one instead:
static <Request,Response> Descriptor.Call<Request,Response> pathCall(String pathPattern, akka.japi.function.Creator<ServiceCall<Request,Response>> methodRef)
The problem is... I don't know how. I haven't seen any example of the use of akka.japi.function.Creator in pathCall.
I tried this:
default Descriptor descriptor() {
return named("blog-query").with(
pathCall("/api/bloggie/tick/:interval", this::tick),
pathCall("/api/bloggie/tock", new Creator<ServiceCall<String, Source<String, ?>>> () {
public ServiceCall<String, Source<String, ?>> create() {
return tock();
}
})
//pathCall("/api/bloggie/newPosts", this::newPosts),
//pathCall("/api/bloggie/postSummaries", this::getPostSummaries)
).withAutoAcl(true);
}
It compiles. But it throws an error at runtime:
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, java.lang.IllegalStateException: Unable to resolve method for service call with ID PathCallId{pathPattern='/api/bloggie/tock'}. Ensure that the you have passed a method reference (ie, this::someMethod). Passing anything else, for example lambdas, anonymous classes or actual implementation classes, is forbidden in declaring a service descriptor.
at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)
while locating com.lightbend.lagom.internal.server.ResolvedServices
Thanks in advance!
I just did some experiments... All compiled, but none of them works....
namedCall("/api/bloggie/tock", this::tock)
Result: Compile success. Runtime: path unknown (no binding (?)).
Then I tried
pathCall("/api/bloggie/tock", () -> this.tock())
Result: exception.
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, scala.MatchError: Request (of class sun.reflect.generics.reflectiveObjects.TypeVariableImpl)
at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)
while locating com.lightbend.lagom.internal.server.ResolvedServices
for parameter 1 at com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry.<init>(ServiceRegistrationModule.scala:55)
at com.lightbend.lagom.internal.server.ServiceRegistrationModule.bindings(ServiceRegistrationModule.scala:29):
Binding(class com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry to self eagerly) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
while locating com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry
Then I tried:
public ServiceCall<NotUsed, Source<String, ?>> tock(Void x);
Result: exception
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, java.lang.IllegalArgumentException: Don't know how to serialize ID class java.lang.Void
at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)
Update: "Solved" (partially). Figured out that this one works:
pathCall("/tock", this::tock)
I can open it using this URL: ws://localhost:9000/tock
So..., I can't have nicely structured URL for those functions that returns stream, when those functions need no param? At least for now (?).
UPDATE: seems like this problem is happening not only with pathCall. I encountered the same problem with rest call. This one doesn't work (no binding):
public ServiceCall<NotUsed, PSequence<PostSummary>> getPostSummaries();
...
restCall(Method.GET, "/api/bloggie/postSummaries", this::getPostSummaries)
This one works:
public ServiceCall<NotUsed, PSequence<PostSummary>> getPostSummaries();
...
restCall(Method.GET, "/postSummaries", this::getPostSummaries)
Thanks!
So firstly, namedCall should only be used if you don't care about the path. You are invoking the service call directly, which means you do care about the path, so you have to use pathCall or restCall.
This should work:
pathCall("/api/bloggie/tock", this::tock)
Also, I think you're not pasting the full errors. Make sure you check right to the bottom of the list of Guice errors, that should explain exactly what the problem is, in many of the cases above, the problem is that you're not passing a method reference, you're passing a lambda, and the error message should say that.

Can protobuf service method return primitive type?

I'm trying to use Google protobuf and i 'm having the next descriptions:
message.proto file:
message Request {
required int32 id = 1;
optional string value = 2;
}
service.proto file:
import "message.proto";
service Service {
rpc request (Request) returns (bool);
}
I'm trying to generate c++ sources and getting an error:
$ protoc service.proto --cpp_out=/tmp/proto/build
service.proto:4:40: Expected message type.
Do i have to return user-defined types only? Are primitive (like bool or string) supported? Can i use primitive types as service method argument (instead of Request in my example)?
No, you cannot use a primitive type as either the request or response. You must use a message type.
This is important because a message type can be extended later, in case you decide you want to add a new parameter or return some additional data.
If you want to return a primitive type, wrap it in a message and return it:
message Name {
string name = 1;
}
In case you don't want to return anything, void I mean, you can just create an empty message:
message Void {}
message Name {
string name = 1;
}
..
service MyService{
rpc MyFunc(Name) returns (Void);
}
You can return scalar datatypes like bool, int, etc by making use of wrappers.proto
service.proto file:
import "message.proto";
import "google/protobuf/wrappers.proto";
service Service {
rpc request (Request) returns (.google.protobuf.BoolValue);
}

Resources