Xamarin forms push notification using azure - azure

I have developed an app with Xamarin Forms and implemented push notification using Azure Notification Hub.
I followed this tutorial from Microsoft site :
https://learn.microsoft.com/en-us/azure/notification-hubs/notification-hubs-backend-service-xamarin-forms
I receive the notification when send it to all ,The problem is when I use the 'Tag' in order to send it to specific user(I register this user with certain tag when he log in the application.)
This is not working and I don't even have an idea how to check where is the problem.
I tried to check out weather the tag is not assigned correctly or not assigned at all but it's not possible to check it.
What should I do?
How can I see all the tags I have in system and who is registered to the tag?
Maybe I didn't understood it correctly but I don't create the Tag before the user register ,I just add it to the Tags List when the user is registered to the system.
public class DeviceInstallation
{
[Required]
public string InstallationId { get; set; }
[Required]
public string Platform { get; set; }
[Required]
public string PushChannel { get; set; }
public IList<string> Tags { get; set; } = Array.Empty<string>(); //Generate tag when register device and add it to list
}
public async Task RegisterDeviceAsync(params string[] tags)
{
var deviceInstallation = DeviceInstallationService?.GetDeviceInstallation(tags);
if (deviceInstallation == null)
throw new Exception($"Unable to get device installation information.");
if (string.IsNullOrWhiteSpace(deviceInstallation.InstallationId))
throw new Exception($"No {nameof(deviceInstallation.InstallationId)} value for {nameof(DeviceInstallation)}");
if (string.IsNullOrWhiteSpace(deviceInstallation.Platform))
throw new Exception($"No {nameof(deviceInstallation.Platform)} value for {nameof(DeviceInstallation)}");
if (string.IsNullOrWhiteSpace(deviceInstallation.PushChannel))
throw new Exception($"No {nameof(deviceInstallation.PushChannel)} value for {nameof(DeviceInstallation)}");
await SendAsync<DeviceInstallation>(HttpMethod.Put, RequestUrl, deviceInstallation)
.ConfigureAwait(false);
var serializedTags = JsonConvert.SerializeObject(tags);
await SecureStorage.SetAsync(CachedTagsKey, serializedTags);
}
The following code is in the server:
public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
return false;
var installation = new Installation()
{
InstallationId = deviceInstallation.InstallationId,
PushChannel = deviceInstallation.PushChannel,
Tags = deviceInstallation.Tags
};
if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
installation.Platform = platform;
else
return false;
try
{
await _hub.CreateOrUpdateInstallationAsync(installation, token);
}
catch
{
return false;
}
return true;
}

Related

Azure ad b2c custom connector "ShowBlockPage" response is not working

I am following the documentation here to return the blocking response https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-api-connector?pivots=b2c-user-flow#example-of-a-blocking-response from api connector to azure ad b2c, however even after constructing the right response as shown in the documentation, I am still not able to show the blocking page for b2c user flow.
Note that this connector gets invoked at sign-in.
I have verified that the response from api which seems correct and looks like below
{
"version": "1.0.0",
"action": "ShowBlockPage",
"userMessage": "You must have a local account registered for Contoso."
}
With this, was hoping to see a blocking page as below (screenshot from docs) but b2c does not show it and goes straight to the connected application.
What did I miss? any pointers would be appreciated. TIA.
Here is my api connector's code
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
// Get the request body
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// If input data is null, show block page
if (data == null)
{
return (ActionResult)new OkObjectResult(new ResponseContent("ShowBlockPage", "There was a problem with your request."));
}
// Print out the request body
log.LogInformation("Request body: " + requestBody);
// check for issuer
if(data.identities != null)
{
string issuer = data.identities[0].issuer;
log.LogInformation("issuer detected: " + issuer);
if(issuer == "github.com")
{
log.LogInformation("Returning an error!");
//return (ActionResult)new BadRequestObjectResult(new ResponseContent("ValidationError", "Please provide a Display Name with at least five characters."));
return (ActionResult)new OkObjectResult(new ResponseContent("ShowBlockPage", "You must have a local account registered for Contoso."));
}
}
// Validation passed successfully, return `Allow` response.
return (ActionResult)new OkObjectResult(new ResponseContent()
{
jobTitle = "This value return by the API Connector"//,
// You can also return custom claims using extension properties.
//extension_CustomClaim = "my custom claim response"
});
}
and here is the ResponseContent class
public class ResponseContent
{
public const string ApiVersion = "1.0.0";
public ResponseContent()
{
this.version = ResponseContent.ApiVersion;
this.action = "Continue";
}
public ResponseContent(string action, string userMessage)
{
this.version = ResponseContent.ApiVersion;
this.action = action;
this.userMessage = userMessage;
if (action == "ValidationError")
{
this.status = "400";
}
}
public string version { get; }
public string action { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string userMessage { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string status { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string jobTitle { get; set; }
}
We ended up creating Microsoft support ticket for this and got response that this is by design. ShowBlockPage response works only with sign-up user flows and not with sign-in user flows.

MS Text Analytics Cognitive Service: how to work with local database?

Microsoft provides a service to analyze text data called Text Analytics Cognitive Service.
Is it possible to use this service with local database? i.e. not in Azure
I work with some large databases and as for me it can be interesting to use it for:
Language detection
Key phrase extraction
Named Entity recognition
Sentiment analysis
Once you pull your data that you would like to detect its language
from your local database, you just need to fetch it then just pass in
below method. It would analysis your value in response.
API Access Keys:
private static readonly string endpointKey = "YourEndPonitKey";
private static readonly string endpoint = "https://YourServiceURL.cognitiveservices.azure.com/text/analytics/v2.1/languages";
Code Snippet:
public async Task<object> DetectLanguageAsync(string InputFromDbOrUser)
{
try
{
DetectedLanguageResponseModel ObjDitectedLanguageResponse = new DetectedLanguageResponseModel();
//Create laguage detection request param
RequestModel objRequestModel = new RequestModel();
objRequestModel.id = "1";
objRequestModel.text = InputFromDbOrUser;
//Made Document List
List<RequestModel> Objdocuments = new List<RequestModel>();
Objdocuments.Add(objRequestModel);
//Bind Request Model
LanguageDetection objRequestList = new LanguageDetection();
objRequestList.documents = Objdocuments;
// Bind and Serialize Request Object
var serializedObject = JsonConvert.SerializeObject(objRequestList);
// Call Language Detection API
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(endpoint);
request.Content = new StringContent(serializedObject, Encoding.UTF8, "application/json");
request.Headers.Add("Ocp-Apim-Subscription-Key", endpointKey);
var response = await client.SendAsync(request);
//Check status code and retrive response
if (response.IsSuccessStatusCode)
{
ResponseModel objResponse = JsonConvert.DeserializeObject<ResponseModel>(await response.Content.ReadAsStringAsync());
//Check Response List
foreach (var item in objResponse.documents)
{
//Checkings Empty Response and Return to Caller
if (objResponse.documents != null)
{
ObjDitectedLanguageResponse.Language = objResponse.documents[0].detectedLanguages[0].name;
return ObjDitectedLanguageResponse;
}
else
{
return "Sorry, I am not able to find a related topic! Would you like me to Bing Search?";
}
}
}
else
{
var result_string = await response.Content.ReadAsStringAsync();
return result_string;
}
}
return ObjDitectedLanguageResponse;
}
catch (Exception ex)
{
throw new NotImplementedException(ex.Message, ex.InnerException);
}
}
Class Used:
public class DetectedLanguage
{
public string name { get; set; }
public string iso6391Name { get; set; }
}
public class DetectedLanguageResponseModel
{
public dynamic Language { get; set; }
}
public class LanguageDetection
{
public List<RequestModel> documents { get; set; }
}
public class RequestModel
{
public string id { get; set; }
public string text { get; set; }
}
public class ResponseDocument
{
public string id { get; set; }
public List<DetectedLanguage> detectedLanguages { get; set; }
}
public class ResponseModel
{
public List<ResponseDocument> documents { get; set; }
public List<object> errors { get; set; }
}
Note: The current limit is 5,120 characters for each document; if you need to analyze larger documents, you can break them up into
smaller chunks for more you could refer official document
Hope that would help. If you need more implementation assistance please have a look on here

Servicestack allow same email for multiple users

I am trying to allow multiple users to have the same email. I extended the OrmLiteAuthRepository overrode the AssertNoExistingUser but it is never called, even though i get the "duplicate email error". I know its hooked up because the getpermissions method is working.
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
protected override void AssertNoExistingUser(IDbConnection db, IUserAuth newUser, IUserAuth exceptForExistingUser = null)
{
//I hate using try catch for simple stuff, it is very slow,
//But this is only during new users being added so low risk for slow
//and the base class should be called to do its native stuff
try
{
base.AssertNoExistingUser(db,newUser);
}
catch (Exception e)
{
if (e.Message.Contains("Email"))
{
//mask duplicate email messages
return;
}
//throw any other errors on new user creation.
throw;
}
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
//Ignore this as we have implemented our own security
// base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
return ss.UserPermissions(Convert.ToInt32(userAuthId));
}
}
ServiceStack can authenticate using either Username or Email, but irrespective of which is used they must be unique in order to uniquely identify the user that is attempting to authenticate.
If you just want to persist the same email address in multiple users you can store it in PrimaryEmail which isn't validated or used in Authentication.

Error when adding Where or OrderBy clauses to Azure Mobile Apps request

I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}

Microsoft bot with Direct line Rest API for newbie of BOT

I am a newbie of Microsoft bot.
My company have their own communication application, and i wanna connect my bot with communication application, my client can use my bot on my campany's communication application. I read about it need to use Direct Line to do it. But i really don't know how to do it.
Does anybody help me ?or give me some suggestions? or any example of that.
Thank you so much.
Please refer documentation about direct line approach for Bot framework.
What you have to do is use https://directline.botframework.com/api/conversations as your endpoint and call those API as shown in the documentation.
Example :- I just tried with ASP.MVC application. I created a text box and button for submit message to bot.
First enable direct link in your bot application. Then remember that secret.
Following code sample shows you how to connect your chat app or your company app with bot you built using bot frame work.
First you need to authorize your access to direct link API.
client = new HttpClient();
client.BaseAddress = new Uri("https://directline.botframework.com/api/conversations/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("BotConnector", "[Your Secret Key Here]");
response = await client.GetAsync("/api/tokens/");
if (response.IsSuccessStatusCode)
If you are success with previous response you can start a new Conversation
Model -
public class Conversation
{
public string conversationId { get; set; }
public string token { get; set; }
public string eTag { get; set; }
}
Code inside controller -
var conversation = new Conversation();
response = await client.PostAsJsonAsync("/api/conversations/",conversation);
if (response.IsSuccessStatusCode)
If you success with this response you will get conversationId and a token to start messaging.
Then pass your message to bot via following code,
Conversation ConversationInfo = response.Content.ReadAsAsync(typeof(Conversation)).Result as Conversation;
string conversationUrl = ConversationInfo.conversationId+"/messages/";
Message msg = new Message() { text = message };
response = await client.PostAsJsonAsync(conversationUrl,msg);
if (response.IsSuccessStatusCode)
If you get a success response, that means you have already sent your message to the bot. Now you need to get the reply message from BOT
To get the message from bot,
response = await client.GetAsync(conversationUrl);
if (response.IsSuccessStatusCode){
MessageSet BotMessage = response.Content.ReadAsAsync(typeof(MessageSet)).Result as MessageSet;
ViewBag.Messages = BotMessage;
IsReplyReceived = true;
}
Here you get a Message set, That means the message you sent and the reply from the Bot. You can now display it in your chat window.
Message Model -
public class MessageSet
{
public Message[] messages { get; set; }
public string watermark { get; set; }
public string eTag { get; set; }
}
public class Message
{
public string id { get; set; }
public string conversationId { get; set; }
public DateTime created { get; set; }
public string from { get; set; }
public string text { get; set; }
public string channelData { get; set; }
public string[] images { get; set; }
public Attachment[] attachments { get; set; }
public string eTag { get; set; }
}
public class Attachment
{
public string url { get; set; }
public string contentType { get; set; }
}
Using those API calls you can easily connect any of your custom chat applications with bot framework. Below is the full code inside one method for you to get idea about how you can archive your goal.
private async Task<bool> PostMessage(string message)
{
bool IsReplyReceived = false;
client = new HttpClient();
client.BaseAddress = new Uri("https://directline.botframework.com/api/conversations/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("BotConnector", "[Your Secret Code Here]");
response = await client.GetAsync("/api/tokens/");
if (response.IsSuccessStatusCode)
{
var conversation = new Conversation();
response = await client.PostAsJsonAsync("/api/conversations/", conversation);
if (response.IsSuccessStatusCode)
{
Conversation ConversationInfo = response.Content.ReadAsAsync(typeof(Conversation)).Result as Conversation;
string conversationUrl = ConversationInfo.conversationId+"/messages/";
Message msg = new Message() { text = message };
response = await client.PostAsJsonAsync(conversationUrl,msg);
if (response.IsSuccessStatusCode)
{
response = await client.GetAsync(conversationUrl);
if (response.IsSuccessStatusCode)
{
MessageSet BotMessage = response.Content.ReadAsAsync(typeof(MessageSet)).Result as MessageSet;
ViewBag.Messages = BotMessage;
IsReplyReceived = true;
}
}
}
}
return IsReplyReceived;
}
Thanks Cheers with you bot.

Resources