According to the Hangire Documentation, allowing authorization to Hangfire dashboard can be done in the following way in C#:
// For ASP.NET Core environments, use the GetHttpContext extension method defined in the Hangfire.AspNetCore package.
public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
// Allow all authenticated users to see the Dashboard (potentially dangerous).
return httpContext.User.Identity.IsAuthenticated;
}
}
// The second step is to pass it to the UseHangfireDashboard method. You can pass multiple filters, and the access will be granted only if all of them return true.
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new [] { new MyAuthorizationFilter() }
});
I'm using Giraffe as web server, and I've tried the following way:
type HangfireAuthorization () =
interface IDashboardAuthorizationFilter with
override _.Authorize (dc: DashboardContext) =
false
type WebApp(context: StatelessServiceContext) =
inherit StatelessService(context)
//....
let configureApp (app: IApplicationBuilder) =
let storageContext = app.ApplicationServices.GetService(typeof<StorageContext>) :?> StorageContext
let hangfireDashboardOptions: DashboardOptions =
let x = DashboardOptions()
x.Authorization <- HangfireAuthorization() // how to correctly set this option?
x
app.UseGiraffeErrorHandler(errorHandler)
.UseCors("AllowCors")
.UseHangfireDashboard("/hangfire", hangfireDashboardOptions)
How can I correctly set the authorization field of the Hangfire DashboardOptions?
I got it working like this:
type HangfireAuthorization () =
interface IDashboardAuthorizationFilter with
override _.Authorize (dc: DashboardContext) =
false
let hangfireDashboardOptions: DashboardOptions =
let x = DashboardOptions()
x.Authorization <- [ HangfireAuthorization() ] // or [| HangfireAuthorization() |]
x
app.UseGiraffeErrorHandler(errorHandler)
.UseCors("AllowCors")
.UseHangfireDashboard("/hangfire", hangfireDashboardOptions)
Related
I have a custom Image composable that uses Coil's rememberAsyncImagePainter.
However, I have another component that uses resources and has logic that is handled separately.
I successfully render the custom resource placeholder, however I'm not sure how I can write a test to check that the actual url image is loaded & visible.
Both the url image and the resource image have different testTags, however in the test, the node with the url image's tag never exists.
Does Coil have any solution to mock the ImageRequest.Builder so that I can guarantee that the URL image successfully loads?
I would prefer to not add any test-related code to the component itself, but if that's the only way, then I would prefer the component to be testable.
According to the official docs https://coil-kt.github.io/coil/image_loaders/#testing, you can create a FakeImageLoader class like this:
class FakeImageLoader(private val context: Context) : ImageLoader {
override val defaults = DefaultRequestOptions()
override val components = ComponentRegistry()
override val memoryCache: MemoryCache? get() = null
override val diskCache: DiskCache? get() = null
override fun enqueue(request: ImageRequest): Disposable {
// Always call onStart before onSuccess.
request.target?.onStart(request.placeholder)
val result = ColorDrawable(Color.BLACK)
request.target?.onSuccess(result)
return object : Disposable {
override val job = CompletableDeferred(newResult(request, result))
override val isDisposed get() = true
override fun dispose() {}
}
}
override suspend fun execute(request: ImageRequest): ImageResult {
return newResult(request, ColorDrawable(Color.BLACK))
}
private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult {
return SuccessResult(
drawable = drawable,
request = request,
dataSource = DataSource.MEMORY_CACHE
)
}
override fun newBuilder() = throw UnsupportedOperationException()
override fun shutdown() {}
}
And you UI test, you can write
#Test
fun testCustomImageComposable() {
Coil.setImageLoader(FakeCoilImageLoader())
setContent {
CutomImageComposable(...)
}
// ... assert image is displayed, etc.
}
This should guarantee the image to be shown every-time the test is executed.
Alternatively, you can mock ImageLoader and mimic the behavior above.
However, I would not recommend mocking ImageLoader, as we do not know whether rememberAsyncImagePainter uses all of the methods, but if you must mock it, it would be worth a try.
From the documentation, I can pass string, integer etc. But how can I pass objects on navigation?
Note: If I set the argument type parcelable then the app crashes with java.lang.UnsupportedOperationException: Parcelables don't support default values..
composable(
"vendor/details/{vendor}",
arguments = listOf(navArgument("vendor") {
type = NavType.ParcelableType(Vendor::class.java)
})
) {
// ...
}
The following workarounds based on navigation-compose version 2.4.0-alpha05.
I found 2 workarounds for passing objects.
1. Convert the object into JSON string:
Here we can pass the objects using the JSON string representation of the object.
Example code:
val ROUTE_USER_DETAILS = "user-details/user={user}"
// Pass data (I am using Moshi here)
val user = User(id = 1, name = "John Doe") // User is a data class.
val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(User::class.java).lenient()
val userJson = jsonAdapter.toJson(user)
navController.navigate(
ROUTE_USER_DETAILS.replace("{user}", userJson)
)
// Receive Data
NavHost {
composable(ROUTE_USER_DETAILS) { backStackEntry ->
val userJson = backStackEntry.arguments?.getString("user")
val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(User::class.java).lenient()
val userObject = jsonAdapter.fromJson(userJson)
UserDetailsView(userObject) // Here UserDetailsView is a composable.
}
}
// Composable function/view
#Composable
fun UserDetailsView(
user: User
){
// ...
}
2. Passing the object using NavBackStackEntry:
Here we can pass data using navController.currentBackStackEntry and receive data using navController.previousBackStackEntry.
Example code:
val ROUTE_USER_DETAILS = "user-details/{user}"
// Pass data
val user = User(id = 1, name = "John Doe") // User is a parcelable data class.
navController.currentBackStackEntry?.arguments?.putParcelable("user", user)
navController.navigate(ROUTE_USER_DETAILS)
// Receive data
NavHost {
composable(ROUTE_USER_DETAILS) { backStackEntry ->
val userObject = navController.previousBackStackEntry?.arguments?.getParcelable<User>("user")
UserDetailsView(userObject) // Here UserDetailsView is a composable.
}
}
// Composable function/view
#Composable
fun UserDetailsView(
user: User
){
// ...
}
Important Note: The 2nd solution will not work if we pop up back stacks on navigate.
Parcelables currently don't support default values so you need to pass your object as String value. Yes it is a work around.. So instead of passing object itself as Parcelize object we can turn that object into JSON (String) and pass it through navigation and then parse that JSON back to Object at destination. You can use GSON for object to json string conversion...
Json To Object
fun <A> String.fromJson(type: Class<A>): A {
return Gson().fromJson(this, type)
}
Object To Json String
fun <A> A.toJson(): String? {
return Gson().toJson(this)
}
User NavType.StringType instead of NavType.ParcelableType..
composable("detail/{item}",
arguments = listOf(navArgument("item") {
type = NavType.StringType
})
) {
it.arguments?.getString("item")?.let { jsonString ->
val user = jsonString.fromJson(User::class.java)
DetailScreen( navController = navController, user = user )
}
}
Now navigate by passing string..
val userString = user.toJson()
navController.navigate(detail/$userString")
EDIT: There is also a limit for the Json-String that you can navigate. If the length of the Json-String is tooo long then the NavController won't recognize your Composable Route eventually throw an exception... Another work around would be to use a Global Variable and set its value in before navigating.. then pass this value as arguments in your Composable Functions..
var globalUser : User? = null // global object somewhere in your code
.....
.....
// While Navigating
click { user->
globalUser = user
navController.navigate(detail/$userString")
}
// Composable
composable( "detail") {
DetailScreen(
navController = navController,
globalUser)
}
NOTE :-> ViewModels can also be used to achieve this..
Let me give you very simple answers.
We have different options like.
Using Arguments but issue with this is that you can't share long or complex objects, only simple types like Int, String, etc.
Now you are thinking about converting objects to JsonString and trying to pass it, but this trick only works for small or easy objects.
Exception look like this:
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri="VERY LONG OBJECT STRING" } cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0x2e9fc7db) route=Screen_A}
Now we have a Parsable Type in navArgument, but we need to put that object in current backStack and need to retrieve from next screen. The problem with this solution is you need keep that screen in your backStack. You can't PopOut your backStack. Like, if you want to popout your Login Screen when you navigate to Main Screen, then you can't retrieve Object from Login Screen to Main Screen.
You need to Create SharedViewModel. Make sure you only use shared state and only use this technique when above two are not suitable for you.
With Arguments:
You can just make this object Serializable and pass it to the backStackEntry arguments, also you can pass String, Long etc :
data class User (val name:String) : java.io.Serializable
val user = User("Bob")
navController.currentBackStackEntry?.arguments?.apply {
putString("your_key", "key value")
putSerializable("USER", user)
)
}
to get value from arguments you need to do next:
navController.previousBackStackEntry?.arguments?.customGetSerializable("USER")
code for customGetSerializable function:
#Suppress("DEPRECATION")
inline fun <reified T : Serializable> Bundle.customGetSerializable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) getSerializable(key, T::class.java)
else getSerializable(key) as? T
}
With savedStateHandle
Sometimes you have nullable arguments, so you can use savedStateHandle:
appState.navController.currentBackStackEntry?.savedStateHandle?.set("USER", user)
and get value:
navController.previousBackStackEntry?.savedStateHandle?.get("USER")
#HiltViewModel
class JobViewModel : ViewModel() {
var jobs by mutableStateOf<Job?>(null)
private set
fun allJob(job:Job)
{
Toast.makeText(context,"addJob ${job.companyName}", Toast.LENGTH_LONG).show()
jobs=job
}
#Composable
fun HomeNavGraph(navController: NavHostController,
) {
val jobViewModel:JobViewModel= viewModel() // note:- same jobViewModel pass
in argument because instance should be same , otherwise value will null
val context = LocalContext.current
NavHost(
navController = navController,
startDestination = NavigationItems.Jobs.route
) {
composable(
route = NavigationItems.Jobs.route
) {
JobsScreen(navController,jobViewModel)
}
composable(
route= NavigationItems.JobDescriptionScreen.route
)
{
JobDescriptionScreen(jobViewModel=jobViewModel)
}
}
}
}
in function argument (jobViewModel: JobViewModel)
items(lazyJobItems) {
job -> Surface(modifier = Modifier.clickable {
if (job != null) {
jobViewModel.allJob(job=job)
navController.navigate(NavigationItems.JobDescriptionScreen.route)
}
Let's take this class:
type Test() =
let mutable Flag1 : bool = false
[<DefaultValue>] val mutable Flag2 : bool
do
Flag1 <- true // that works
Flag2 <- true // nope... why?
member this.SetFlag =
Flag1 <- true // no 'this' instance? does that mean it's static?
this.Flag2 <- true // that works now, but still no way to set a default value
Why do I need [<DefaultValue>] when I want to be able to set the value to whatever myself?
and then now:
type Test2() =
Inherit(Test)
do
Flag1 <- true // not accessible
Flag2 <- true // not happening either
member this.SetFlag2 =
Flag1 <- true // not accessible
this.Flag2 <- true // ok
so I could do:
member val Flag1 : bool with get, set
but why isn't the syntax val this.Flag1 since it's part of the instance?
and.. I can't access that from the constructor.
Can anyone explain how this works? because it's very confusing.
It looks like the usefulness of constructors is quite low if you can't set things in there. Or, am I missing something?
And I can't construct an instance of the class and set variables though a new() constructor since the last statement needs to return the instance, so I can't create an instance, set everything I need and then return it. Or, is it possible?
Also, how can I have a field that:
Is an instance field, not static
I can initialize to whatever I want in the constructor
Is mutable
Is accessible from member functions
like a plain field in C# in essence.
What I am trying to implement is:
I need to have a timer object that belongs to the class instance and can be initialized in the constructor. Class methods need to have access to the timer and the timer's callback needs to have access to class fields.
In pseudo C#, I'd have something like this:
Class A
{
public Timer MyTimer;
public bool Flag1, Flag2;
A()
{
MyTimer = new Timer(Callback);
}
public void StartTimer()
{
MyTimer.Start();
}
public void Callback()
{
if (Flag1) Flag2 = true;
}
}
public class B : A
{
public void DoStuff()
{
Flag1 = True;
}
}
What you need is to specify a type instance alias for all it's body:
type Test() (*here is the part you need: *) as this =
// ...
// and then you can access your public mutable field by type instance alias:
do this.Flag2 <- true
Also, in your second inherited class (Test2), you can access the Flag2 field with the same syntax. And, by the way Field1 can't be accessed in Test2 class because it's private because you have defined it using let binding
I am working with spring-data-neo4j and i finally made auditing in my project.
This is my config for audit
#Configuration
#EnableNeo4jAuditing
class AuditingConfig {
#Bean
fun auditorProvider(): AuditorAware<Long> = SpringSecurityAuditAwareImpl()
}
class SpringSecurityAuditAwareImpl : AuditorAware<Long> {
override fun getCurrentAuditor(): Optional<Long> {
val authentication: Authentication? = SecurityContextHolder.getContext().authentication
if(authentication?.isAuthenticated != true ||
authentication is AnonymousAuthenticationToken)
return Optional.empty()
val userPrincipal = authentication.principal as UserPrincipal
return Optional.ofNullable(userPrincipal.id)
}
}
and this is my audit class
#JsonIgnoreProperties(
value = ["createdAt", "updatedAt"],
allowGetters = true
)
abstract class DateAudit : Serializable {
#CreatedDate
val createdAt: LocalDateTime? = null
#LastModifiedDate
val updatedAt: LocalDateTime? = null
}
It work perfectly when entity make first.
But when update entity, the "createdAt" property is null.
I know the #CreatedDate is just work when entity created.
After create, it set null .
In JPA, this problem can be avoided by #Column(updatable = false).
So, I want to know spring-data-neo4j has annotation like #Column(updatable = false)
or solution avoid this problem.
You should init the "createdAt" property
I've done interception using Castle.DynamicProxy and StructureMap 2.6 API but now can't do it using StructureMap 3.0. Could anyone help me find updated documentation or even demo? Everything that I've found seems to be about old versions. e.g. StructureMap.Interceptors.TypeInterceptor interface etc.
HAHAA! I f***in did it! Here's how:
public class ServiceSingletonConvention : DefaultConventionScanner
{
public override void Process(Type type, Registry registry)
{
base.Process(type, registry);
if (type.IsInterface || !type.Name.ToLower().EndsWith("service")) return;
var pluginType = FindPluginType(type);
var delegateType = typeof(Func<,>).MakeGenericType(pluginType, pluginType);
// Create FuncInterceptor class with generic argument +
var d1 = typeof(FuncInterceptor<>);
Type[] typeArgs = { pluginType };
var interceptorType = d1.MakeGenericType(typeArgs);
// -
// Create lambda expression for passing it to the FuncInterceptor constructor +
var arg = Expression.Parameter(pluginType, "x");
var method = GetType().GetMethod("GetProxy").MakeGenericMethod(pluginType);
// Crate method calling expression
var methodCall = Expression.Call(method, arg);
// Create the lambda expression
var lambda = Expression.Lambda(delegateType, methodCall, arg);
// -
// Create instance of the FuncInterceptor
var interceptor = Activator.CreateInstance(interceptorType, lambda, "");
registry.For(pluginType).Singleton().Use(type).InterceptWith(interceptor as IInterceptor);
}
public static T GetProxy<T>(object service)
{
var proxyGeneration = new ProxyGenerator();
var result = proxyGeneration.CreateInterfaceProxyWithTarget(
typeof(T),
service,
(Castle.DynamicProxy.IInterceptor)(new MyInterceptor())
);
return (T)result;
}
}
The problem here was that SM 3.* allows interception for known types, i.e. doing something like this:
expression.For<IService>().Use<Service>().InterceptWith(new FuncInterceptor<IService>(service => GetProxyFrom(service)));
But what if you'd like to include the interception logic inside your custom scanning convention where you want to intercept all instances of type with specific signature (types having name ending on 'service', in my case)?
That's what I've accomplished using Expression API and reflection.
Also, I'm using here Castle.DinamicProxy for creating proxy objects for my services.
Hope someone else will find this helpful :)
I find the best place to go for any new versions is directly to the source.
If it's written well, then it will include test cases. Thankfully structuremap does include test cases.
You can explore the tests here
In the meantime I've written an example of an Activator Interceptor, and how to configure it.
static void Main()
{
ObjectFactory.Configure(x =>
{
x.For<Form>().Use<Form1>()
.InterceptWith(new ActivatorInterceptor<Form1>(y => Form1Interceptor(y), "Test"));
});
Application.Run(ObjectFactory.GetInstance<Form>());
}
public static void Form1Interceptor(Form f)
{
//Sets the title of the form window to "Testing"
f.Text = "Testing";
}
EDIT:
How to use a "global" filter using PoliciesExpression
[STAThread]
static void Main()
{
ObjectFactory.Configure(x =>
{
x.Policies.Interceptors(new InterceptorPolicy<Form>(new FuncInterceptor<Form>(y => Intercept(y))));
});
Application.Run(ObjectFactory.GetInstance<Form>());
}
private static Form Intercept(Form form)
{
//Do the interception here
form.Text = "Testing";
return form;
}