I have a scenario, where I am using the State pattern. Every Start has an Action defined. We start with an initial state and an initial Action and then keep going until the Action becomes null. If the action becomes null, then we stop the execution. Also, the transition between the states is not sequential. We execute the Action on the State that is returned by the previous state.
To replicate the same, I have implemented the below recursion -
SelectionMachine
private Flux<SelectionState> run(OfferSelectionRequest request) {
Flux<SelectionStateResult> initialSelectionStateResult = Flux.just(new SelectionStateResult(selectionRequested, ACTION_START, new SelectionContext(request)));
return go(initialSelectionStateResult);
}
private Flux<SelectionState> go(Flux<SelectionStateResult> resultFlux) {
val result = resultFlux.flatMap(selectionStateResult -> {
if (selectionStateResult.getAction().isEmpty())
return Flux.just(selectionStateResult);
else {
selectionStateResult.getAction().get();
return go(move(selectionStateResult.getState(), selectionStateResult.getAction().orElse(null), selectionStateResult.getContext().getRequest()));
}
});
return result;
}
private Flux<SelectionStateResult> move(SelectionState state, Action action, OfferSelectionRequest request) {
Flux<SelectionStateResult> result = Flux.empty();
if (ACTION_START.equals(action)) {
result = state.start(request);
} else if (ACTION_FETCH_OFFERS.equals(action)) {
state.fetchOffers(request);
} else {
result = Flux.just(new SelectionStateResult(state));
}
return result;
}
SelectionStateResult
package com.paytmlabs.adtech.adtechdecisionengine.domain.selection;
import com.paytmlabs.adtech.adtechdecisionengine.domain.result.OfferResult;
import com.paytmlabs.adtech.adtechdecisionengine.exception.SelectionError;
import com.paytmlabs.adtech.adtechdecisionengine.selection.machine.InternalSelectionStateMachine;
import com.paytmlabs.adtech.adtechdecisionengine.selection.state.SelectionState;
import lombok.Data;
import java.util.List;
import java.util.Optional;
#Data
public class SelectionStateResult {
private SelectionState state;
private Optional<InternalSelectionStateMachine.Action> action;
private SelectionContext context;
private SelectionError error;
private List<OfferResult> selectedOffers; // TODO: OfferResult needs to be created
public SelectionStateResult(SelectionState state) {
this.state = state;
this.action = Optional.empty();
}
public SelectionStateResult(SelectionState state, InternalSelectionStateMachine.Action action) {
this.state = state;
this.action = Optional.ofNullable(action);
}
public SelectionStateResult(SelectionState state, InternalSelectionStateMachine.Action action, SelectionContext context) {
this.state = state;
this.action = Optional.ofNullable(action);
this.context = context;
}
public SelectionStateResult(SelectionState state, SelectionError error) {
this.state = state;
this.action = Optional.empty();
this.context = null;
this.error = error;
}
public SelectionStateResult(SelectionState state, SelectionContext context) {
this.state = state;
this.action = Optional.empty();
this.context = context;
this.error = null;
}
}
When I try to do this, I am getting an error in the go method-
no instance(s) of type variable(s) R exist so that SelectionState conforms to Publisher<? extends R>
Need some guidance on how can I implement the following use case -
We start from an initial state and action.
After the state terminates, it returns a state and an action.
If action and state are not null, we execute the action of the returned state.
If the action is null, we just return the state and that is the end of the execution.
Related
I have a custom method decorator like this.
export function CustomDecorator() {
return applyDecorators(
UseGuards(JwtAuthGuard)
);
}
Inside the Custom Decorator, I want to get the Request Header but not sure how to get the Request Instance?
You won't be able to get the ExectuionContext object or the Request object in a class or method decorator, because these decorators are run immediately at the moment of import. What should be done instead is to make a SuperGuard that does have the ExecutionContext available to it. This SuperGuard should have all of the other guards injected into it via the constructor and depending on the header you should call/return the result from the guard called. Something like this:
#Injectable()
export class SuperGuard implements CanActivate {
constructor(
private readonly jwtAuthGuard: JwtAuthGuard,
private readonly googleAuthGuard: GoogleAuthGuard,
) {}
canActivate(context: ExecutionContext) {
const req = context.switchToHttp().getRequest();
if (req.headers['whatever'] === 'google') {
return this.googleAuthGuard.canActivate(context);
} else {
return this.jwtAuthGuard.canActivate(context);
}
}
}
I managed to access the execution context within decorator using Inject inside decorator's factory.
Here is my decorator that swallows errors produced by method and returns predefined value in case of exception.
import { Injectable, Scope, Inject, ExecutionContext } from '#nestjs/common';
import { CONTEXT } from '#nestjs/graphql';
#Injectable({ scope: Scope.REQUEST })
export class ExceptionsHandler {
public constructor(#Inject(CONTEXT) private readonly context: ExecutionContext) {}
private integrationsRequestErrors: unknown[] = [];
public handle(error: unknown): void {
// ADD error to context if necessary
this.integrationsRequestErrors.push(error);
}
}
export const ErrorSwallower = (options: {
serviceImplementation: string;
defaultValue: unknown;
errorMessage?: string;
}): MethodDecorator => {
const { defaultValue, integration } = options;
const Injector = Inject(ExceptionsHandler);
return (target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {
Injector(target, 'exceptionsHandler');
const originalMethod = descriptor.value;
descriptor.value = function (...args: unknown[]) {
const exceptionHandler = this.experiment as ExceptionsHandler;
try {
const result = originalMethod.apply(this, args);
if (result && result instanceof Promise) {
return result.catch((error: unknown) => {
exceptionHandler.handle({ error, integration });
return defaultValue;
});
}
return result;
} catch (error) {
exceptionHandler.handle({ error, integration });
return defaultValue;
}
};
};
};
and here is the code above put into action:
#Injectable()
export class ExampleService {
#ErrorSwallower({ serviceImplementation: 'ExampleClass', defaultValue: [] })
private async getSomeData(args: IGetSomeDataArgs): Promise<ISomeData[]> {
throw new Error('Oops');
}
}
I try to call to a function that it a part from Provider, but I noticed that when
the call is inside firebase.auth().onAuthStateChanged function all the providers are undefined.
#Ionic3.9.2
#cordova-8.1.2 (cordova-lib#8.1.1)
That's my Code :
constructor(public global:global,public navCtrl: NavController, public navParams: NavParams, public app: App, public dataProvider: DataProvider,
public loadingProvider: LoadingProvider, public alertProvider: AlertProvider, public alertCtrl: AlertController, public firebaseProvider: FirebaseProvider, public modalCtrl: ModalController) { }
ionViewDidLoad() {
console.log(this.dataProvider);
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
console.log(user);
this.currentUser=user;
this.tab = "friends";
this.title = "Friends";
this.searchFriend = '';
console.log(this.dataProvider); /* -> undefined*/
if (this.dataProvider.getRequests(this.currentUser != null)) { /* the console return to me an error :TypeError: Cannot read property 'getRequests' of undefined */
console.log(this.dataProvider.getRequests(this.currentUser));
// console.log(this.dataProvider.getRequests(firebase.auth().currentUser.uid));
console.log("if ");
// console.log(this.currentUser.uid);
console.log(this.currentUser);
this.dataProvider.getRequests(this.currentUser.uid).snapshotChanges().subscribe((requestsRes) => {
let requests = requestsRes.payload.val();
console.log(requests);
if (requests != null) {
if (requests.friendRequests != null && requests.friendRequests != undefined)
this.friendRequestCount = requests.friendRequests.length;
else this.friendRequestCount = 0
}
else this.friendRequestCount = 0;
console.log(this.friendRequestCount);
});
this.getFriends();
}
} else {
// No user is signed in.
console.log('no User');
}
});
I am sure that I miss something and be grateful for some help Thank you!
Ok I found the solution
Explanation:
It's all about "this" keyword
this.dataProvider in the callback is not the same this.dataProvider outside the callback function.
To refer to the same this.dataProvider inside and outside the callback function, you just need to create a function and pass the dataProvider as a parameter.
constructor(public global:global,public navCtrl: NavController, public navParams: NavParams, public app: App, public dataProvider: DataProvider,
public loadingProvider: LoadingProvider, public alertProvider: AlertProvider, public alertCtrl: AlertController, public firebaseProvider: FirebaseProvider, public modalCtrl: ModalController) {
this.ionViewDidLoad(this.dataProvider); // <--- with this (mandatory)
}
ionViewDidLoad(dataProvider: DataProvider) {
console.log(dataProvider); // <-- defined with or without this
firebase.auth().onAuthStateChanged(function (user) {
.........
console.log(dataProvider); /* without this -> NOT undefined anymore :) */
})
I have a CursorLoader that observes a directory with this URI:
uriDirectory = content://com.myapp.stocks/stocks
and another CursorLoader that observes an item with this URI:
uriItem = content://com.myapp.stocks/stocks/GOOG
When I update uriItem and call getContext().getContentResolver().notifyChange(uriItem , null); in my ContentProvider, how can I prevent it from notifying uriDirectory as well?
Thanks!
Edit: So my solution so far is just to have a boolean that is set to true when I notify a uriItem. Then when it notifies the parent, uriDirectory, it will see that the boolean is true and won't perform any operations. After, I set the boolean back to false.
You can write your own CursorLoader. The default cursor loaders register a content observer via Cursor.RegisterContentObserver(ContentObserver observer). Instead, we want to use registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer).
I'm not sure if you are using the support library CursorLoader but for the greatest applicability, that's what I'm using.
The only changes from the stock android versions are in loadInBackground(). You should create an entire class instead of just extending android's and overriding loadInBackground because it protects you from future changes made to Android. Be advised that this will not use any notification url you set for the cursor in your ContentProvider unless you the device is KitKat or newer
Uri notificationUri;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
notificationUri = cursor.getNotificationUri();
} else {
notificationUri = mUri;
}
getContext().getContentResolver().registerContentObserver(
notificationUri != null ? notificationUri : mUri,
false, //don't notify for descendants
mObserver
);
Full class descendantChangeIgnoringCursorLoader.java:
package com.innomatixdata.busscan.utils;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.ContentResolverCompat;
import android.support.v4.content.Loader;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public class DescendantChangeIgnoringCursorLoader extends AsyncTaskLoader<Cursor> {
final Loader.ForceLoadContentObserver mObserver;
Uri mUri;
String[] mProjection;
String mSelection;
String[] mSelectionArgs;
String mSortOrder;
Cursor mCursor;
android.support.v4.os.CancellationSignal mCancellationSignal;
/* Runs on a worker thread */
#Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new android.support.v4.os.OperationCanceledException();
}
mCancellationSignal = new android.support.v4.os.CancellationSignal();
}
try {
Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
Uri notificationUri;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
notificationUri = cursor.getNotificationUri();
} else {
notificationUri = mUri;
}
getContext().getContentResolver().registerContentObserver(
notificationUri != null ? notificationUri : mUri,
false, //don't notify for descendants
mObserver
);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
#Override
public void cancelLoadInBackground() {
super.cancelLoadInBackground();
synchronized (this) {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
}
}
/* Runs on the UI thread */
#Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Creates an empty unspecified CursorLoader. You must follow this with
* calls to {#link #setUri(Uri)}, {#link #setSelection(String)}, etc
* to specify the query to perform.
*/
public DescendantChangeIgnoringCursorLoader(Context context) {
super(context);
mObserver = new Loader.ForceLoadContentObserver();
}
/**
* Creates a fully-specified CursorLoader. See {#link ContentResolver#query(Uri, String[],
* String, String[], String) ContentResolver.query()} for documentation on the meaning of the
* parameters. These will be passed as-is to that call.
*/
public DescendantChangeIgnoringCursorLoader(Context context, Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new Loader.ForceLoadContentObserver();
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
*
* Must be called from the UI thread
*/
#Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
public Uri getUri() {
return mUri;
}
public void setUri(Uri uri) {
mUri = uri;
}
public String[] getProjection() {
return mProjection;
}
public void setProjection(String[] projection) {
mProjection = projection;
}
public String getSelection() {
return mSelection;
}
public void setSelection(String selection) {
mSelection = selection;
}
public String[] getSelectionArgs() {
return mSelectionArgs;
}
public void setSelectionArgs(String[] selectionArgs) {
mSelectionArgs = selectionArgs;
}
public String getSortOrder() {
return mSortOrder;
}
public void setSortOrder(String sortOrder) {
mSortOrder = sortOrder;
}
#Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.print(prefix); writer.print("mUri="); writer.println(mUri);
writer.print(prefix); writer.print("mProjection=");
writer.println(Arrays.toString(mProjection));
writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
writer.print(prefix); writer.print("mSelectionArgs=");
writer.println(Arrays.toString(mSelectionArgs));
writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
}
}
I keep getting null, even though the package is added. I looked at the source, turns out StubPackageManager is always returning null for that and there is no way to override the entire PackageManager class.
In setup:
MockPackageManager mockPackageManager = new MockPackageManager(
Robolectric.getShadowsAdapter());
RuntimeEnvironment.setRobolectricPackageManager(mockPackageManager);
Subclass:
class MockPackageManager extends DefaultPackageManager {
public MockPackageManager(ShadowsAdapter shadowsAdapter) {
super(shadowsAdapter);
}
#Override
public String getNameForUid(int uid) {
switch (uid) {
case UID_A:
return NAME_A;
case UID_B:
return NAME_B;
default:
return null;
}
}
#Override
public boolean isPermissionRevokedByPolicy(String s, String s1) {
return false;
}
}
I got the following function
Map<MyClass, String> someFunction() {
Map<MyClass, String> result = new HashMap<>();
return result.put(new MyClass("someString"), "someOtherString"));
}
The implementation of MyClass looks like the following:
public class MyClass{
String string;
public MyClass(String string) {
this.string = string;
}
public void setString(String string) {
this.string = string;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((string== null) ? 0 : string.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MyClass other = (MyClass) obj;
if (string== null) {
if (other.string!= null) {
return false;
}
} else if (!string.equals(other.string)) {
return false;
}
return true;
}
}
In my test I am doing the following:
#Test
public void test() {
Map<MyClass, String> outcome = classUnderTest.someFunction();
assertThat(outcome.get(new MyClass("someString")), is("someOtherString"));
}
But this test fails, because actual is null.
If I try the following:
assertThat(outcome.keySet(), hasItem(MY_CLASS));
this also fails, telling me, that these are different intantiations. I even tried to debug my test, but it never reaches the equals method. Can you tell me what is happening here?
Are you sure, that your method doesn't modify the objecT? I think, that someFunction replaces the string in MyClass. That causes that your object of MyClass return another hashCode.
A HashMap works like that:
put:
compute hashCode of the key. Store value under that hashCode
get:
compute hashCode of the key. Search for a value with that hashCode. If there is a value, then maybe call equals.
So: never use mutable values as key! Otherwise, you may lose your data (or make it difficult to resolve)
Just try to execute this test, it should be green
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class SomeTest {
Map<MyClass, String> someFunction() {
Map<MyClass, String> result = new HashMap<>();
result.put(new MyClass("someString"), "someOtherString");
return result;
}
#Test
public void test() {
Map<MyClass, String> outcome = someFunction();
assertThat(outcome.get(new MyClass("someString")), is("someOtherString"));
}
public static class MyClass {
String string;
public MyClass(String string) {
this.string = string;
}
public void setString(String string) {
this.string = string;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((string == null) ? 0 : string.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MyClass other = (MyClass) obj;
if (string == null) {
if (other.string != null) {
return false;
}
} else if (!string.equals(other.string)) {
return false;
}
return true;
}
}
}
but if you modify MyClass object after it was added to Map, the test became red:
Map<MyClass, String> someFunction() {
Map<MyClass, String> result = new HashMap<>();
MyClass key = new MyClass("someOldString");
result.put(key, "someOtherString");
key.setString("someString");
return result;
}
In your function you are returning null
From the JavaDoc for HashMap:
public V put(K key, V value)
Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
Specified by:
put in interface Map<K,V>
Overrides:
put in class AbstractMap<K,V>
Parameters:
key - key with which the specified value is to be associated
value - value to be associated with the specified key
Returns:
the previous value associated with key, or null if there was no mapping for key. (A null return can also indicate that the map previously associated null with key.)
put() returns what used to be there, not what you just put there