Why do I need to use pipe with shareReplay? - rxjs

I'm confused why the following doesn't compile:
login(email: string, password: string): Observable<any> {
return this.http.post<authObj>('/users/login',{"email": email, "password": password})
.shareReplay()
}
(I get error Property 'shareReplay' does not exist on type 'Observable<authObj>'.ts(2339)).
Note for reference: authObj is:
interface authObj {
"username": string,
"email": string
}
but the following compiles fine:
login(email: string, password: string): Observable<any> {
return this.http.post<authObj>('/users/login',{"email": email, "password": password})
.pipe(
tap(res=> this.setSession),
shareReplay()
);
}
I would have thought that from the standpoint of shareReplay both constructs are similar/identical in terms of the calling object/input, so I would have thought either of the above should work. Why does only the second construct compile? Tks!

The biggest change from RxJs 5 to 6 was tree shakability. In RxJs 5 all the operators were part of the observable class. This means that when you import the Observable class into your project you get the lot, every operator no matter if your were using them or not. This tends to bloat the size of your application even if you only need a small amount of RxJs functionality.
With RxJs 6 the operators were moved out into pipeable functions, this meant that you could no longer use the fluent syntax of the methods but came with the huge advantage of your build only needing to include the functionality you require rather than all of RxJs. This reduced build sizes dramatically.
For more information take a look at:
Official documentation about "Pipeable Operators"

Related

Is it bad to use a variable from outside the observable pipe within an operator?

Is using a variable from outside an observable within an operator considered a (significantly) bad practice?
createObservableExample1(parameter1: string, obs$: Observable<string>): Observable<string> {
return obs$.pipe(
map( x => {
const returnValue = `${parameter1}, ${x}`;
return returnValue;
})
);
}
I understand you can do something like this:
createObservableExample2(parameter1: string, obs$: Observable<string>): Observable<string> {
return combineLatest([
of(parameter1),
obs$
]).pipe(
map( (x, y) => {
const returnValue = `${x}, ${y}`;
return returnValue;
})
);
}
But is it worth it?
Does this just come down to accessing variables from outside the scope of anonymous function? Would this force the context of the enclosing method to exist for longer than it should? I remember a code tool I used to use for C# complaining about something similar to this. I have found somewhat related topics by searching for, "anonymous functions and closures", but as of yet, nothing really discussing the scenario explained above.
I ask because I have been creating some relatively complex observables that have enormous operator chains, and constantly adding the needed variables, using combineLatest and of, from the parent scope can make the code even harder to follow.
When I teach Reactive programming to neophytes, I try to make them grasp : Do not break the reactivity by having uneccessary side effects :
no input that from a state (for example using a class or instance property
no storing outside value.
There is none of these red flags in your example. Your function is pure & idempotent with both implementation, go with what ever you like and if possible be consistant within your code base !

what is ContractPromise? near-sdk-as to near-sdk-rs comparison

I've found this function in an assemblyscript project for a NEAR contract:
export function assert_single_promise_success(): void {
const x = ContractPromise.getResults()
assert(x.length == 1, "Expected exactly one promise result")
assert(x[0].succeeded, "Expected PromiseStatus to be successful")
}
What does ContractPromise.getResults() do exactly? How should implement the same thing in rust?
here is something similar in Rust from one of the examples in the SDK repo
require!(env::promise_results_count() == 2);
let data0: Vec<u8> = match env::promise_result(0) {
PromiseResult::Successful(x) => x,
_ => env::panic_str("Promise with index 0 failed"),
};
I'm going to give an answer, comments taken directly from the implementation of ContractPromise.getResults(), which can be found here. The implementation also has an example on how to use the function, which may be useful.
Method to receive async (one or multiple) results from the remote
contract in the callback.
#returns An array of results based on the number of promises the
callback was created on. If the callback using then was scheduled
only on one result, then one result will be returned.

AWS Step Function error handling for Go Lambda

I cannot find a detailed explanation of how to define the error condition matcher in the Step function, based on the error returned by the Go handler.
The handler is a bog-standard Go function, returns an error if it gets a 503 from an upstream service:
func HandleHotelBookingRequest(ctx context.Context, booking HotelBookingRequest) (
confirmation HotelBookingResponse, err error) {
...
if statusCode == http.StatusServiceUnavailable {
err = errors.New("TransientError")
} else {
I can control what the function returns, and how it formats the string; I cannot find any real information about what to use here (or in a Catch clause, for that matter), so tht this matches the above:
"Retry": [
{
"ErrorEquals": [
"TransientError"
],
"BackoffRate": 1,
"IntervalSeconds": 1,
"MaxAttempts": 3,
"Comment": "Retry for Transient Errors (503)"
}
]
When I test the Lambda in the Console, this is what I get (as expected) when the upstream returns a 503:
{
"errorMessage": "TransientError",
"errorType": "errorString"
}
And I have the distinct impression (but not quite sure how to validate this) that if I change to:
"ErrorEquals": [
"errorString"
],
the Retry works (at least, looking at the CloudWatch logs, I can see the transient errors being logged, but the Step function eventually succeeds).
I cannot find much documentation on this but:
would it be possible to match on the actual error message (I saw that the API Gateway allows to do that, using a RegEx);
if that's not possible, should I return a different "error type", instead of error
Thanks in advance!
Finally solved the riddle; in the end, it was trivial and fairly identical to the JavaScript approach (which (a) gave me the hint and (b) is widely documented in examples); however, as I was unable to find a Go-specific answer anywhere (in AWS -expansive, good, detailed- documentation, Google, here) I am posting it here for future reference.
TL;DR - define your own implementation of the error interface and return an object of that type, instead of the bog-standard fmt.Error(), then use the type name in the ErrorEquals clause.
A very basic example implementation is shown in this gist.
To test this, I have created an ErrorStateMachine (JSON definition in the same gist) and selected a different catcher based on the ErrorEquals type:
{
"ErrorEquals": [
"HandlerError"
],
"Next": "Handler Error"
}
Testing the Step Function with different Outcome inputs, causes different paths to be chosen.
What I guess tripped me off was that I am a relative beginner when it comes to Go and I hadn't realized that errorString is the actual type of the error interface returned by the errors.New() method, which is used inside fmt.Errorf():
// in errors/errors.go
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
I had naively assumed that this was just something that AWS named.
An interesting twist (which is not really ideal) is that the actual error message is "wrapped" in the Step function output and may be a bit cumbersome to parse in subsequent steps:
{
"Error": "HandlerError",
"Cause": "{\"errorMessage\":\"error from a failed handler\",\"errorType\":\"HandlerError\"}"
}
It would have certainly been a lot more developer-friendly to have the actual error message (generated by Error()) to be emitted straight into the Cause field.
Hope others find this useful and won't have to waste time on this like I did.

RxSwift how to skip map depending on previous result?

I am trying to download some json, parse it, check some information in the json and depending one the result continue processing or not.
What's the most RxSwift idiomatic way of doing this?
URLSession.shared.rx
.data(request:request)
.observe(on: ConcurrentDispatchQueueScheduler(qos: .background))
.flatMap(parseJson) // into ModelObject
.flatMap(checkModel) // on some condition is there any way to jump into the onCompleted block? if the condition is false then execute processObject
.map(processObject)
.subscribe(
onError: { error in
print("error: \(error)")
}, onCompleted: {
print("Completed with no error")
})
.disposed(by: disposeBag)
where parseJsonis something like:
func parseJson(_ data: Data) -> Single<ModelObject>
checkModel does some checking and if some conditions are fullfilled should complete the sequence without ending in processObject
func checkModel(_ modelObject: ModelObject) -> Single<ModelObject> {
//probably single is not what I want here
}
And finally processObject
func processObject(_ modelObject: ModelObject) -> Completable {
}
This is a bit of a tough question to answer because on the one hand you ask a bog simple question about skipping a map while on the other hand you ask for "most RxSwift idiomatic way of doing this," which would require more changes than simply jumping the map.
If I just answer the basic question. The solution would be to have checkModel return a Maybe rather than a Single.
Looking at this code from a "make it more idiomatic" perspective, a few more changes need to take place. A lot of what I'm about to say comes from assumptions based on the names of the functions and expectations as to what you are trying to accomplish. I will try to call out those assumptions as I go along...
The .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) is likely not necessary. URLSession already emits on the background.
The parseJson function probably should not return an Observable type at all. It should just return a ModelObject. This assumes that the function is pure; that it doesn't perform any side effect and merely transforms a Data into a ModelObject.
func parseJson(_ data: Data) throws -> ModelObject
The checkModel function should probably not return an Observable type. This really sounds like it should return a Bool and be used to filter the model objects that don't need further processing out. Here I'm assuming again that the function is pure, it doesn't perform any side-effect, it just checks the model.
func checkModel(_ modelObject: ModelObject) -> Bool
Lastly, the processObject function presumably has side effects. It's likely a consumer of data and therefore shouldn't return anything at all (i.e., it should return Void.)
func processObject(_ modelObject: ModelObject)
Udpdate: In your comments you say you want to end with a Completable. Even so, I would not want this function to return a completable because that would make it lazy and thus require you to subscribe even when you just want to call it for its effects.
You can create a generic wrap operator to make any side-effecting function into a Completable:
extension Completable {
static func wrap<T>(_ fn: #escaping (T) -> Void) -> (T) -> Completable {
{ element in
fn(element)
return Completable.empty()
}
}
}
If the above functions are adjusted as discussed above, then the Observable chain becomes:
let getAndProcess = URLSession.shared.rx.data(request:request)
.map(parseJson)
.filter(checkModel)
.flatMap(Completable.wrap(processObject))
.asCompletable()
The above will produce a Completable that will execute the flow every time it's subscribed to.
By setting things up this way, you will find that your base functions are far easier to test. You don't need any special infrastructure, not even RxText to make sure they are correct. Also, it is clear this way that parseJson and checkModel aren't performing any side effects.
The idea is to have a "Functional Core, Imperative Shell". The imperative bits (in this case the data request and the processing) are moved out to the edges while the core of the subscription is kept purely functional and easy to test/understand.

how to decode a byte slice to different structs elegantly

var response Response
switch wrapper.Domain {
case "":
response = new(TypeA)
case "TypeB":
response = new(TypeB)
case "TypeC":
response = new(TypeC)
case "TypeD":
response = new(TypeD)
}
_ = decoder.Decode(response)
As shown in the code snippet, I got enough information from the Domain filed of wrapper to determine the type of response, and for each type, the following operations are performed:
create a new instance of that type using new
use the decoder to decode the byte slice to the instance created in step 1
I am wondering if there is a way to make the first step more generic and get rid of the switch statement.
A bit about your code
As per discussion in comments, I would like to share some experience.
I do not see nothing bad in your solution, but there are few options to improve it, depends what you want to do.
Your code looks like classic Factory. The Factory is a pattern, that create object of a single family, based on some input parameters.
In Golang this is commonly used in simpler way as a Factory Method, sometimes called Factory function.
Example:
type Vehicle interface {};
type Car struct {}
func NewCar() Vehicle {
return &Car{}
}
But you can easily expand it to do something like you:
package main
import (
"fmt"
"strings"
)
type Vehicle interface {}
type Car struct {}
type Bike struct {}
type Motorbike struct {}
// NewDrivingLicenseCar returns a car for a user, to perform
// the driving license exam.
func NewDrivingLicenseCar(drivingLicense string) (Vehicle, error) {
switch strings.ToLower(drivingLicense) {
case "car":
return &Car{}, nil
case "motorbike":
return &Motorbike{}, nil
case "bike":
return &Bike{}, nil
default:
return nil, fmt.Errorf("Sorry, We are not allowed to make exam for your type of car: \"%s\"", drivingLicense)
}
}
func main() {
fmt.Println(NewDrivingLicenseCar("Car"))
fmt.Println(NewDrivingLicenseCar("Tank"))
}
Above code produces output:
&{} <nil>
<nil> Sorry, We are not allowed to make exam for your type of car: "Tank"
So probably you can improve your code by:
Closing into a single function, that takes a string and produces the Response object
Adding some validation and the error handling
Giving it some reasonable name.
There are few related patterns to the Factory, which can replace this pattern:
Chain of responsibility
Dispatcher
Visitor
Dependency injection
Reflection?
There is also comment from #icza about Reflection. I agree with him, this is used commonly, and We cannot avoid the reflection in our code, because sometimes things are so dynamic.
But in your scenario it is bad solution because:
You lose compile-time type checking
You have to modify code when you are adding new type, so why not to add new line in this Factory function?
You make your code slower(see references), it adds 50%-100% lose of performance.
You make your code so unreadable and complex
You have to add a much more error handling to cover not trivial errors from reflection.
Of course, you can add a lot of tests to cover a huge number of scenarios. You can support TypeA, TypeB, TypeC in your code and you can cover it with tests, but in production code sometime you can pass TypeXYZ and you will get runtime error if you do not catch it.
Conclusion
There is nothing bad with your switch/case scenario, probably this is the most readable and the easiest way to do what you want to do.
Reference
Factory method: https://www.sohamkamani.com/golang/2018-06-20-golang-factory-patterns/
Classic book about patterns in programming: Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma and his band of four, ISBN: 978-0201633610
Reflection benchmarks: https://gist.github.com/crast/61779d00db7bfaa894c70d7693cee505

Resources