Using the ABP Suite, I am trying to add a navigation property to the IdentityUser class. In my case, I want to add a State entity as part of adding address properties to the IdentityUser. I have added several extended properties to the IdentityUser at this point, but I'm kind of scratching my head on this one. I could go in and add the State by hand, but that would not generate the UI controls. Does anyone have a method for doing this?
You can add property by using Object Extensions and set them as navigation properties by adding other entity Id as foreign key.
Associate a department to a user from docs:
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<Guid>(
"DepartmentId",
property =>
{
property.UI.Lookup.Url = "/api/departments";
property.UI.Lookup.DisplayPropertyName = "name";
}
);
});
});
And the "/api/departments" endpoint:
[Route("api/departments")]
public class DepartmentController : AbpController
{
[HttpGet]
public async Task<ListResultDto<DepartmentDto>> GetAsync()
{
...
}
}
Related
I'm sorry if it sound a bit newbie, but i'm stuck trying to get working the AutocompleteHandler.
Following the documentation, i tried to create a very basic autocomplete system :
public class ExampleAutocompleteHandler : AutocompleteHandler
{
public override async Task<AutocompletionResult> GenerateSuggestionsAsync(IInteractionContext context, IAutocompleteInteraction autocompleteInteraction, IParameterInfo parameter, IServiceProvider services)
{
// Create a collection with suggestions for autocomplete
IEnumerable<AutocompleteResult> results = new[]
{
new AutocompleteResult("Name1", "value111"),
new AutocompleteResult("Name2", "value2")
};
// max - 25 suggestions at a time (API limit)
return AutocompletionResult.FromSuccess(results.Take(25));
}
}
In the module :
public class ItemModule : InteractionModuleBase
{
// you need to add `Autocomplete` attribute before parameter to add autocompletion to it
[SlashCommand("command_name", "command_description")]
public async Task ExampleCommand([Summary("parameter_name"), Autocomplete(typeof(ExampleAutocompleteHandler))] string parameterWithAutocompletion)
=> await RespondAsync($"Your choice: {parameterWithAutocompletion}");
}
I tried to register the handler as a Singleton, but nothing worked. It doesnt display the list when i type the commands.
I appreciate some help. Thanks in advance!
Indeed the documentation is currently missing this.
Just like Slash Commands, you need to handle the Autocompletion event and forward it to your interaction service.
On your discord client, handle the event
_client.AutocompleteExecuted += async (SocketAutocompleteInteraction arg) => {
var context = new Discord.Interactions.InteractionContext(_client, arg, arg.Channel);
await _interactionService.ExecuteCommandAsync(context,services: _serviceProvider);
};
I’m working with Xamarin and FireBase, I have the next problem:
I have Users in my database and each one has a list of items. I need to retrieve the list of items of the current user, and the problem is that I don’t know how to replace the key of each user in the tree, (I just hard-wrote it). This is the snippet :
var products = (await client.Child("Users").Child("-MXf6T19md1UOTVvHWRU").Child("Items")
.OnceAsync<Item>())
.Select(f => new Item
{ . . .
Here is a picture of the database tree.
How do I replace the Child("-MXf6T19md1UOTVvHWRU") by the current branch where I’m at?, taking into account that I have to access another Child next to that. Of course I have access to the Username value that is stored in preferences.
I would appreciate some enlightenment, thanks in advance.
I need to retrieve the list of items of the current user, and the problem is that I don’t know how to replace the key of each user in the tree, (I just hard-wrote it).
You could use the code below to get all the items first and then get the specific item.
public async Task<List<User>> GetAllUsers()
{
return (await firebase
.Child("Users")
.OnceAsync<User>()).Select(item => new User
{
Name = item.Object.Name,
UserId = item.Object.UserId,
}).ToList();
}
public async Task<User> GetUser(int userId)
{
var allUsers = await GetAllUsers();
await firebase
.Child("Users")
.OnceAsync<User>();
return allUsers.Where(a => a.UserId == userId).FirstOrDefault();
}
Model:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
}
You could download the similar source code from the GitHub.
https://github.com/susairajs/Xamarin-Firebase-RealtimeDatabase
trying to setup a multi-tenant site using ABP.io framework 3.1.
I am trying to set the <meta keywords (amongst other tags) in the page html head. I am attempting to get the values from a database field for the current tenant so the meta keywords will be specific for the tenant.
I tried to follow the sample that is available here: https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface#layout-hooks where they inject a google analytics script code into the head tag.
this is fine, as it is static text, but when i try to load the partial page with a model it throws an error of expecting a different model to that which is passed in.
So far i have the Notification View Componet
Public class MetaKeywordViewComponent : AbpViewComponent
{
public async Task<IViewComponentResult> InvokeAsync() {
return View("/Pages/Shared/Components/Head/MetaKeyword.cshtml"); //, meta);
}
}
and the cshtml page
#using MyCompany.MyProduct.Web.Pages.Shared.Components.Head
#model MetaKeywordModel
#if (Model.SiteData.Keywords.Length > 0)
{
<meta content="#Model.SiteData.Keywords" name="keywords" />
}
and the cshtml.cs file as
public class MetaKeywordModel : MyProductPageModel
{
private readonly ITenantSiteDataAppService _tenantSiteDataAppService;
public TenantSiteDataDto SiteData { get; private set; }
public MetaKeywordModel(ITenantSiteDataAppService tenantSiteDataAppService)
{
_tenantSiteDataAppService = tenantSiteDataAppService;
}
public virtual async Task<ActionResult> OnGetAsync()
{
if (CurrentTenant != null)
{
SiteData = await _tenantSiteDataAppService.GetSiteDataAsync();
}
return Page();
}
}
but when i run the program i get the following error.
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook.LayoutHookViewModel', but this ViewDataDictionary instance requires a model item of type 'MyCompany.MyProduct.TenantData.Dtos.TenantSiteDataDto'.
How do i pass the data from my database into the page to be rendered if i can't use my model?
Any help tips or tricks would be greatly appreciated.
Regards
Matty
ViewComponent is different from the razor page.
See https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1#view-components
You should inject the service in view component class directly. like:
public class MetaKeywordViewComponent : AbpViewComponent
{
private readonly ITenantSiteDataAppService _tenantSiteDataAppService;
public MetaKeywordViewComponent(ITenantSiteDataAppService tenantSiteDataAppService)
{
_tenantSiteDataAppService = tenantSiteDataAppService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
return View("/Pages/Shared/Components/Head/MetaKeyword.cshtml",
await _tenantSiteDataAppService.GetSiteDataAsync());
}
}
In addition, you can refer https://github.com/abpframework/abp/blob/42f37c5ff01ad853a5425d15539d4222cd0dab69/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/PageAlertsViewComponent.cs
I'm attempting to employ a custom endpoint that adds a specific claim based on a parameter passed to LocalApiController.
I'm following help at:
http://docs.identityserver.io/en/latest/topics/add_apis.html
I configured startup.cs:
services.AddLocalApiAuthentication(principal =>
{
principal.Identities.First().AddClaim(new Claim("additional_claim", "additional_value"));
return Task.FromResult(principal);
});
and my receiving controller:
[Route("localApi")]
[Authorize(LocalApi.PolicyName)]
public class LocalApiController : ControllerBase
{
public IActionResult Get()
{
var claims = from c in User.Claims select new { c.Type, c.Value };
return new JsonResult(claims);
}
}
[Authorize(LocalApi.PolicyName)] works and fires the helper configured in startup.cs which logically fires before the controllers get method.
I don't see how I can pass the controller's {id} parameter to the helper method (that will become the value of the claim I'm to add, replacing the "additional_value" constant).
Any suggestions?
I'm trying to test that the model returned from my Nancy application is as expected. I have followed the docs here but whenever I call the GetModel<T> extension method it throws a KeyNotFoundException.
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
I know what the error means but I'm failing to see why it's being thrown.
Here's my module
public class SanityModule : NancyModule
{
public SanityModule()
{
Get["sanity-check"] = _ => Negotiate.WithModel(new SanityViewModel { Id = 1 })
.WithStatusCode(HttpStatusCode.OK);
}
}
my view model
public class SanityViewModel
{
public int Id { get; set; }
}
and here's my test
[TestFixture]
public class SanityModuleTests
{
[Test]
public void Sanity_Check()
{
// Arrange
var browser = new Browser(with =>
{
with.Module<SanityModule>();
with.ViewFactory<TestingViewFactory>();
});
// Act
var result = browser.Get("/sanity-check", with =>
{
with.HttpRequest();
with.Header("accept", "application/json");
});
var model = result.GetModel<SanityViewModel>();
// Asset
model.Id.ShouldBeEquivalentTo(1);
}
}
Debugging this test shows that the module is hit and completes just fine. Running the application shows that the response is as expected.
Can anyone shed some light on this?
Thanks to the lovely guys, albertjan and the.fringe.ninja, in the Nancy Jabbr room we've got an explanation as to what's going on here.
TL;DR It makes sense for this to not work but the error message should be more descriptive. There is a workaround below.
The issue here is that I am requesting the response as application/json whilst using TestingViewFactory.
Let's take a look at the implementation of GetModel<T>();
public static TType GetModel<TType>(this BrowserResponse response)
{
return (TType)response.Context.Items[TestingViewContextKeys.VIEWMODEL];
}
This is simply grabbing the view model from the NancyContext and casting it to your type. This is where the error is thrown, as there is no view model in NancyContext. This is because the view model is added to NancyContext in the RenderView method of TestingViewFactory.
public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
// Intercept and store interesting stuff
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWMODEL] = model;
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWNAME] = viewName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULENAME] = viewLocationContext.ModuleName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULEPATH] = viewLocationContext.ModulePath;
return this.decoratedViewFactory.RenderView(viewName, model, viewLocationContext);
}
My test is requesting json so RenderView will not be called. This means you can only use GetModel<T> if you use a html request.
Workaround
My application is an api so I do not have any views so changing the line
with.Header("accept", "application/json");
to
with.Header("accept", "text/html");
will throw a ViewNotFoundException. To avoid this I need to implement my own IViewFactory. (this comes from the.fringe.ninja)
public class TestViewFactory : IViewFactory
{
#region IViewFactory Members
public Nancy.Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
viewLocationContext.Context.Items[Fixtures.SystemUnderTest.ViewModelKey] = model;
return new HtmlResponse();
}
#endregion
}
Then it is simply a case of updating
with.ViewFactory<TestingViewFactory>();
to
with.ViewFactory<TestViewFactory>();
Now GetModel<T> should work without needing a view.