MS Graph Service Client - How to prove a subscription was created? - c#

I have the following code: (which used to work and really should work from what I understand from MS' documentation)
[Route("msgraphsubscription")]
[HttpPost]
[AllowAnonymous]
public async Task<String> msgraphsubscription()
{
try{
this.userId = "<guid>"
GraphServiceClient graphClient = await GenerateGraphAuthToken(this.accountManagerParameters);
var subscription = new Subscription
{
ChangeType = "updated",
NotificationUrl= notificationURL,
Resource = $"users/{userId}/drive/root",
ExpirationDateTime = DateTimeOffset.Parse("2021-04-23T18:23:45.9356913Z"),
ClientState = "secretClientValue",
LatestSupportedTlsVersion = "v1_2"
};
await graphClient.Subscriptions
.Request()
.AddAsync(subscription);
return $"New subscriptionID: {subscription.Id.ToString()}";
}
catch(Exception ex){
Console.Write(ex);
return "";
}
When I run this code, I get the following error on the return statement in the try{} block:
{System.NullReferenceException: Object reference not set to an instance of an object.
at msgraphtests.msgraphsubscription() in /src/demos/msgraph/SubscriptionManager.cs:line 132}
error CS1733: Expected expression
What's Broken / Not working
The code actually creates a subscription. But... it doesn't return the subscription ID.
What I've Checked
Verified data. Using Postman, I have created a subscription request using the exact same user, and notification url. I pasted both notification URLs from the POSTMAN request and from my app into a text editor, along with both user ids. Then I do a search and verify that the URL from postman actually matches the one from the vscode file. So i'm fairly certain it's not a typo in those fields.
I put a breakpoint though on the return statement, and checked out the contents of my "subscription" variable and this is what it shows:
{Microsoft.Graph.Subscription}
AdditionalData [IDictionary]:null
ApplicationId [string]:null
ChangeType [string]:"updated"
ClientState [string]:"secretClientValue"
CreatorId [string]:null
EncryptionCertificate [string]:null
EncryptionCertificateId [string]:null
ExpirationDateTime:{2021-04-23 6:23:45 PM +00:00}
Resource [string]:"users//drive/root"
But in postman, i see this new subscription:
{
"id": "a5234fffd-75f0-3364-bd12-ddddassf",
"resource": "users/<userguid>/drive/root",
"applicationId": "asdfasdf-97ec-1b33-8888-asdfasdf",
"changeType": "updated",
"clientState": null,
"notificationUrl": "<mynotificationURL>",
"notificationQueryOptions": null,
"lifecycleNotificationUrl": null,
"expirationDateTime": "2021-04-23T18:23:45.9356913Z",
"creatorId": "asdfr-52f5-4822-fffs-afwerwer",
"includeResourceData": null,
"latestSupportedTlsVersion": "v1_2",
"encryptionCertificate": null,
"encryptionCertificateId": null
},
I changed the code to explicitly request the subscription.Id to be returned like this:
await graphClient.Subscriptions
.Request()
.Select(subscription => new {
subscription.Id,
subscription.ExpirationDateTime
})
.AddAsync(subscription);
return $"New subscriptionID: {subscription.ToString()}";
There are no errors... but the id with a value of Null is still being returned to me.
Question
Does anyone know of another way to "prove" the subscription was created using the subscription object itself? (what artifacts can i check in the subscription object?) Did something change on the MS end? cuz this used to work just fine.
THanks.

By looking at your code, it seems that you're expecting that Microsoft's code updates the Subscription object you pass into .AddAsync() method.
However, looking at Microsoft's implementation of the method, they are returning a whole new object back. So, you should do the following:
Subscription subscription = await graphClient.Subscriptions
.Request()
.AddAsync(subscription);

Related

stripe SessionCreateOptions not persisting client_reference_id that is passed to it

Just migrated to Stripe.com. I am creating a checkout session programmatically. See code snippet below. When I test, the User.Identity.GetUserId() comes back with a value and it is sent to stripe. However, when end user completes the payment, Stripe.com is not sending back the client_reference_id (it is null) in the event checkout.session.completed that I am listening to.
I get back my client_reference_id when I do payment links (send via querystring)
What am I doing wrong?
[HttpPost]
[AllowAnonymous]
public ActionResult SendToCheckout(ProcessPaymentViewModel model)
{
StripeConfiguration.ApiKey = _apiSecret;
var options = new SessionCreateOptions
{
ClientReferenceId = User.Identity.GetUserId(),
SuccessUrl = ConfigurationManager.AppSettings["BaseUrl"] + "/PaymentComplete",
CancelUrl = ConfigurationManager.AppSettings["BaseUrl"] + "/Subscribe",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
Price = model.PriceId,
Quantity =long.Parse(model.Quantity)
},
},
Mode = "payment",
};
var service = new SessionService();
var session = service.Create(options);
return Redirect(session.Url);
}
reviewed stripe.com documentation. It appears I am setting it correctly. The other questions posted is one is really not answered and the other one says it should be in that webhook event. I dumped the values and it should client_reference_id: null
The code you shared looks correct and it's almost certain that you are not setting a value when you think you are.
The best path forward is to hardcode a value in your code and you should see that it works as expected and that the problem is the value you put in. What I would do is hardcode AAAA, confirm it's there, and then concat AAAA and the value in your variable and another string like AAAA-<userid>-BBBB and see that you get AAAA--BBBB because your string is null or empty.
This isn't a Stripe bug, that feature works as expected and is used widely but I've tested it quickly to confirm.
You can also look at the response on the Session creation after your code and just print session.ClientReferenceId and see that it's null right now.

Authentication in Dialogflow API V2 using C#

I have .NET Web API Project for the fulfillment API as our webhook in my Dialogflow agent. In our Post method of the controller, after getting the request from Dialogflow, I implement the explicit authentication as shown in the Google Cloud documentation for C#.
//jsonFileName is the name of the serviceAccountKey json generated from the Google Cloud Platform that's encrypted internally
public bool AuthExplicit(string projectId, string jsonFileName)
{
try
{
string JsonCredential = DecryptHelper.Decrypt(jsonFileName);
var credential = GoogleCredential.FromJson(JsonCredential).CreateScoped(LanguageServiceClient.DefaultScopes);
var channel = new Grpc.Core.Channel(
LanguageServiceClient.DefaultEndpoint.ToString(),
credential.ToChannelCredentials());
var client = LanguageServiceClient.Create(channel);
AnalyzeSentiment(client);
if (client != null)
{
return true;
}
else
{
return false;
}
}
internal void AnalyzeSentiment(LanguageServiceClient client)
{
var response = client.AnalyzeSentiment(new Document()
{
Content = "Authenticated.",
Type = Document.Types.Type.PlainText
});
var sentiment = response.DocumentSentiment;
string score = $"Score: {sentiment.Score}";
string magnitude = $"Magnitude: {sentiment.Magnitude}";
}
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything, and the projectId parameter is never used to authenticate. GCP docs are quite confusing, since when there is an AuthExplicit() that uses projectId, it uses it as a parameter for the buckets and only prints this on the console.
It works fine, until we test the service account key with a different agent. Expected output is that authentication would fail, but somehow it still passes.
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything,
Does client.AnalyzeSentiment() return an empty response? Does the call hang forever?
It works fine, until we test the service account key with a different agent.
What is a different agent? A different User-Agent header?
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
What does 'the Post method' refer to? What is the 'it' that would only return a boolean?

Cannot access child value on JValue in Azure Mobile App

I recently implemented custom authentication with Azure Mobile App - All the server side works fine and also my web application which is using that mobile app service is working fine. I tested the server-side in details with POSTMAN and with different scenarios, everything works fine until I try to LoginAsync on Xamarin.
When I pass email and password in POSTMAN, I get the following response as a clear indication that it is working
but when I send a request from my app using LoginAsync I get the following error.
Cannot access child value on Newtonsoft.Json.Linq.JValue
My code to send request is fairly simple as following
public async Task<bool> Authenticate()
{
string username = "todo#gmail.com";
string password = "todo";
string message = string.Empty;
var success = false;
var credentials = new JObject
{
["email"] = username,
["password"] = password
};
try
{
MobileServiceUser user = await client.LoginAsync("CustomAuth", credentials);
if (user != null)
{
success = true;
CreateAndShowDialog("OK", "Auth");
}
}
catch (Exception ex)
{
CreateAndShowDialog(ex, "Auth Error");
}
return success;
}
where I am calling it as follows
private MobileServiceClient client;
client = new MobileServiceClient(applicationURL);
await Authenticate();
Any idea why I am getting Cannot access child value on Newtonsoft.Json.Linq.JValue error?
Cheers
EDIT POST
As a workaround, I am temporarily using InvokeApiAsync with JObject.FromObject instead of LoginAsync
await client.InvokeApiAsync("/.auth/login/CustomAuth", JObject.FromObject(credentials), HttpMethod.Post, null);
I am still not sure why LoginAsync does not work - Until I find a solution I will keep using InvokdeApiAsync as a workaround
AFAIK, your initialization for credentials is correct. For the below error:
Cannot access child value on Newtonsoft.Json.Linq.JValue
I checked your testing result via POSTMAN and found that you did not return userId to your client. The essential properties returned to your client would look like as follows:
{
"authenticationToken":"***",
"user":{
"userId":"***"
}
}
When using MobileServiceClient.LoginAsync, the client SDK would internally invoke LoginAsync() method under MobileServiceAuthentication.cs as follows:
JToken authToken = JToken.Parse(response);
// Get the Mobile Services auth token and user data
this.Client.CurrentUser = new MobileServiceUser((string)authToken["user"]["userId"]);
this.Client.CurrentUser.MobileServiceAuthenticationToken = (string)authToken[LoginAsyncAuthenticationTokenKey];
You would find that it would try to extract the userId property under user to construct the MobileServiceUser instance and assign to MobileServiceClient.CurrentUser.

Google Calendar push notifications callback troubleshooting

I've implemented a c# app which creates calendar events using the calendar API.
In order to keep the calendar in sync with our local db I've created a watch on the calendar. The following code does this and, as far as I can see, a watch is created.
Channel watchChannel = new Channel()
{
Id = ConfigurationManager.AppSettings["GOOGLE_CALENDAR_WATCH_NAME"],
Type = "web_hook",
Address = ConfigurationManager.AppSettings["GOOGLE_CALENDAR_SYNC_TRIGGER_CALLBACK_URL"],
Expiration = (unixTimestamp + NINETY_DAYS_IN_SECONDS) * 1000 //milliseconds
};
try
{
logger.Debug("Creating calendar watch with the name " +
ConfigurationManager.AppSettings["GOOGLE_CALENDAR_WATCH_NAME"] + " for calendar with id " + remoteCalendarId);
Channel returnChannel = service.Events.Watch(watchChannel, remoteCalendarId).Execute();
My problem is that the callback URL isn't getting called (i have confirmed ownership of the domain and authenticated it for the user, that shouldn't be the issue).
How do I debug this? Is there anywhere I can look at the attempts google is
making to call the callback URL?
I say that as far as I can see everything is created ok, but maybe I'm wrong, which property in the
returnChannel should I be looking at?
Is there any way to list all created watches/channels for a particular calendar? If so, which API is that exposed through?
04.04 - A bit more information:
These are the parameters set on the outgoing call (watchChannel) and return-object (returnChannel).
watchChannel
Address "https://a.domain.com/api/schedule/syncDbAndSchedule"
ETag = null
Expiration = 1491309746000
Id = "my_id_watch_dev"
Kind = null
Params__ = null
Payload = null
ResourceId = null
ResourceUri = null
Token = null
Type = "web_hook"
returnChannel
Address = null
ETag = null
Expiration = 1491309746000
Id = "my_id_watch_dev"
Kind = "api#channel"
Params__ = null
Payload = null
ResourceId = "t6uxfXzXXXXXXXXXXsC9ZEqMdzU"
ResourceUri = "https://www.googleapis.com/calendar/v3/calendars/a.domain.com_nsqpp2XXXXXX93olf0ul40sphg#group.calendar.google.com/events?maxResults=250&alt=json"
Token = null
Type = null
Looking at it again I've got a few more questions:
Can I be sure the above returned a HTTP-200 response? The client-libs seem to abstract away the actual request/response and I can't find any trace of it in the objects I'm looking at. As the 4xx responses I've gotten have been transformed into exceptions that's what I'd expect for any non-200 response but can I be sure of that?
Is there really no way to track the attempts google is making whilst calling the callback URL? Since there seems to be no way to get a hold of a created watch it kind of surprises me there is no GUI where I can track this. Makes hunting for errors really hard.
Code to authenticate
I use the following code to authenticate the system user and then make it act in the guise of a 'normal' non-system account (since a pure system-account seemed a tricky way to go if you actually wanted to look at the calendar too).
ServiceAccountCredential credential =
GoogleCredential.FromJson(GOOGLE_SYSTEM_USER_AUTH_INFORMATION)
.CreateScoped(Scopes)
.UnderlyingCredential as ServiceAccountCredential;
var userField =
typeof(ServiceAccountCredential).GetField("user", BindingFlags.NonPublic | BindingFlags.Instance);
userField?.SetValue(credential, GOOGLE_CALENDAR_USERNAME); //Act in the guise of the normal user GOOGLE_CALENDAR_USERNAME
service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
on a whim I visited the ResourceURI returned. It gives me
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
Is
this the status for the watch? If so, why didn't I get a 403 whilst creating it?
Am I really Unautenticated or is it just that my request through a browser is?
The callback url must be https (certificate) otherwise it will not be called!

stripe.net - how to process subscriptionService.Get with no results

The stripe.com API only returns active subscriptions. I want to verify when I delete a subscription.
So this is going to return an error. I am not sure how to code for it.
I would prefer to make this call based on the subscriptionId. Will this cause an exception or will it return an error code?
Retrieving a subscription
var subscriptionService = new StripeSubscriptionService();
StripeSubscription stripeSubscription = subscriptionService.Get(*subscriptionId*);
Another options which is somewhat of a hack is to return all the subscriptions for the given customer and test to see if the subscriptionId was returned.
List all subscriptions for a customer
var subscriptionService = new StripeSubscriptionService();
IEnumerable<StripeSubscription> responses = subscriptionService .List(*customerId*);
foreach( var response in responses )
{
if (response.subscriptionId == subscriptionId)
{
// subscription exists and was not deleted
exit;
}
}
Per their documentation, https://stripe.com/docs/api they have 2 different APIs. You're trying to use the RESTful API, which is for retrieving information on demand.
They also have a WebHooks API, which requires you have an endpoint listening on your site which can accept event notifications. You configure these through your Dashboard with them.
The event type you're looking for specifically is probably the customer.subscription.deleted event, but there's a lot more you can do with them and I'd encourage you to explore all of those Webhooks.
I can't offer a code sample, as I don't use their service.
The strip.net example shows the subscriptionService.Cancel as a methed:
var subscriptionService = new StripeSubscriptionService();
subscriptionService.Cancel(*customerId*, *subscriptionId*);
But you can also use it as a function and it returns the subscription object.
var subscriptionService = new StripeSubscriptionService();
StripeSubscription stripeSubscription = subscriptionService.Cancel(*customerId*, *subscriptionId*);
If (stripeSubscription.Status != "canceled")
{
//subscription not cancelled
// take action
}
Per Stripe API docs:
Returns:
The canceled subscription object. Its subscription status will be set to "canceled" unless you've set at_period_end to true when canceling, in which case the status will remain "active" but the cancel_at_period_end attribute will change to true.One of the fiels is .status , witch is set to canceled.

Categories

Resources