Azure AD Graph API issue: Specified $skiptoken is invalid - azure-active-directory

I get the following exception when trying to fetch AAD group transitive members using the Graph API:
Specified $skiptoken is invalid.
Here is my code:
private IEnumerable<Microsoft.Graph.User> GetGroupMembers(AccessLevel al) {
var usersCollectionPage = AsyncHelper.RunSync(() => Client.Groups[al.Id].TransitiveMembers.Request().GetAsync());
foreach (var user in usersCollectionPage.CurrentPage.OfType<Microsoft.Graph.User>()) {
yield return user;
}
while (usersCollectionPage.NextPageRequest != null) {
var page = usersCollectionPage;
usersCollectionPage = AsyncHelper.RunSync(() => page.NextPageRequest.GetAsync());
foreach (var user in usersCollectionPage.CurrentPage.OfType<Microsoft.Graph.User>()) {
yield return user;
}
}
}
It fails on page.NextPageRequest.GetAsync().
I am using Microsoft.Graph version 4.11.
This used to work. I suspect it stopped working after upgrading the Graph SDK.
The issue happens in a production environment where there are enough group members to trigger paging.
Has anyone else seen the issue?

This was fixed in Microsoft.Graph.Core 2.0.7: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/345

Related

Google.Cloud.AppEngine.V1 client libraries and traffic splitting in .NET

I am trying to use the Client Libraries provided by Google to move traffic from one version of an app in AppEngine to another. However, the documentation for doing this just talks about using the rest API and not the client libraries.
Here is some example code:
var servicesClient = Google.Cloud.AppEngine.V1.ServicesClient.Create();
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = "apps/myProject/services/myService";
var updateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask = updateMask;
// See below for what should go here...
var updateResponse = servicesClient.UpdateService(updateServiceRequest);
My question is what format do I use for the update mask?
According to the documentation I should put in:
split {"split": { "allocations": { "newVersion": 1 } } }
But when I try: updateMask.Paths.Add(#"split { ""split"": { ""allocations"": { ""myNewVersion"": 1 } } }");
... I get the exception:
"This operation is only supported on the following field(s): [labels, migration_config, network_settings, split, tag_to_target_map], but got field(s): [split { "split": { "allocations": { "myNewVersion": 1 } } }] from the update request.
Any ideas where I should put the details of the split in the field mask object? The property Paths just seems to be a collection of strings.
The examples for these libraries in Google's doco is pretty poor :-(
I raised a support ticket with Google and despite them suggesting a solution which didn't work exactly (due to trying to assign a string to the UpdateMask which needs a FieldMask object), I managed to use it to find the correct solution.
The code should be:
// appService is a previously retrieved Service object from the ListServices method
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = appService.Name;
updateServiceRequest.UpdateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask.Paths.Add("split");
appService.Split.Allocations.Clear();
appService.Split.Allocations["newServiceVerison"] = 1;
updateServiceRequest.Service = appService;

Datastudio Community Connector - Add filter

I have a site with hundreds of members who would like to see activity relating to their products. We use datastudio at the moment, creating a report manually for a few who have asked.
We would like to be able to send out a single report that grabs the member details from the url and sets the report to that member. We followed the datastudio docs https://developers.google.com/datastudio/solution/viewers-cred-with-3p-credentials but it's not very clear
function getAuthType() {
var response = { type: 'NONE' };
return response;
}
function getConfig(request) {
var cc = DataStudioApp.createCommunityConnector();
var config = cc.getConfig();
config
.newTextInput()
.setId('token')
.setName('Enter user token')
.setAllowOverride(true);
config.setDateRangeRequired(false);
config.setIsSteppedConfig(false);
return config.build();
}
function getFields(request) {
var cc = DataStudioApp.createCommunityConnector();
var fields = cc.getFields();
var types = cc.FieldType;
fields.newDimension()
.setId('tokenValue')
.setType(types.TEXT);
return fields;
}
function getSchema(request) {
var fields = getFields(request).build();
return { schema: fields };
}
function getData(request) {
var token = request.configParams.token;
}
Has anyone set up a community connector that would allow multiple users to see a single report but only see what's specific to them?
I'm not sure if the token is being set property. It displays as the placeholder only. Is there a way to be sure what value my parameter is assigned?
We haven't got the the point of passing a url parameter. What we would like to do is pass the token value (Member details) to an existing filter. Is this possible in a community connector?
You can use the Filter by email address feature to filter your data based on the viewer's email address. This works out of the box and won't require you to build a custom connector.
Alternatively, if you do want to build a custom connector, follow this guide that seems more suitable for your use case.

Can't retrieve more then 50 records from Azure MobileServiceClient (Node.js)

So I'm building an Apache Cordova app (Android), angularjs based, and using the WindowsAzure MobileServiceClient library to retrieve data from my Easy Table's, created in a SQL Database.
This works! Until I'd like to retrieve more then 50 records.
So I added the .take(100) to my table-read. Still 50 records..
Then I thought, maybe the take function doesn't work at all, so I changed the amount to 5, and I only got 5 records. So the take function is somewhat working, but not above 50 records.
Since it's a node.js backend, how do I increase the pagesize?
Here's my current code:
msc.tables.pokemonType = null;
msc.tables.pokemon = null;
msc.init = function () {
console.log('MobileServiceClient initializing...');
var mobileServiceClient = new WindowsAzure.MobileServiceClient("https://blablablabla");
msc.tables.pokemonType = mobileServiceClient.getTable('PokemonType');
msc.tables.pokemon = mobileServiceClient.getTable('Pokemon');
console.log('MobileServiceClient initialized!');
}
msc.getAllPokemonTypes = function() {
console.log('getAllPokemonTypes - called');
return msc.tables.pokemonType.read().then(function (pokemonTypes) {
console.log('getAllPokemonTypes - done');
return pokemonTypes;
}, msc.handleError);
}
msc.getAllPokemons = function () {
console.log('getAllPokemons - called');
return msc.tables.pokemon.take(100).read().then(function (pokemons) {
console.log('getAllPokemons - done');
return pokemons;
}, msc.handleError);
}
According the source code of table operations of Mobile Apps in node.js, the read operation ultimately receives context.query which is a queryjs object, which contains a take() function which can limit the number of items returned to the specified number.
Additionally, the take() function is contained in the mobile app server sdk, so it doesn't work on your client end code.
You can do some modification on your Easy Tables scripts, E.G.
table.read(function (context) {
context.query.take(100);
return context.execute();
});

How to access any other's google calendar using google php api

I need access any other user's google calendar freebusy using google php api. Can any one help me step by step process to implement that. I have already checked the https://developers.google.com/google-apps/calendar/v3/reference/freebusy/query#response but nothing fruitful I got.
My Approach
session_start();
require_once ('libraries/Google/autoload.php');
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile");
$client->addScope(Google_Service_Calendar::CALENDAR);
$client->addScope(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAccessType('offline');
$service = new Google_Service_Oauth2($client);
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
exit;
}
/************************************************
If we have an access token, we can make
requests, else we generate an authentication URL.
************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
} else {
$authUrl = $client->createAuthUrl();
}
$client->setApplicationName("My Calendar"); //DON'T THINK THIS MATTERS
$client->setDeveloperKey($api_key); //GET AT AT DEVELOPERS.GOOGLE.COM
$cal = new Google_Service_Calendar($client);
$calendarId = 'any_other_user_email#gmail.com';
$freebusy_req = new Google_Service_Calendar_FreeBusyRequest();
$freebusy_req->setTimeMin($minTime);
$freebusy_req->setTimeMax($maxTime);
$freebusy_req->setTimeZone($time_zone);
$freebusy_req->setCalendarExpansionMax(10);
$freebusy_req->setGroupExpansionMax(10);
$item = new Google_Service_Calendar_FreeBusyRequestItem();
$item->setId($email);
$freebusy_req->setItems(array($item));
$query = $cal->freebusy->query($freebusy_req);
$response_calendar = $query->getCalendars();
$busy_obj = $response_calendar[$email]->getBusy();
But I am getting blank in terms of free busy.
Just make sure the calendar is public, then try google calendar freebusy method. It will definitely work.

Error when trying to insert an element to gcloud DB on App Engine with Dart

I am making a variation of the Client Server Example that just returns JSON strings using the Redstone framework. It has 3 routes:
/ => get the whole list or names
/add/name => add "name" to the list and gets the list
/remove/name => removes "name" from the list and gets the list
When I test locally everything works fine, however, when I deploy to App Engine I get an error when trying to add an element to the gcloud db. The error is
Exception: Tried to insert 1 entities, but response seems to indicate
we inserted 0 entities.
package:appengine/src/api_impl/raw_datastore_v3_impl.dart 416:11
DatastoreV3RpcImpl.commit. dart:isolate
_RawReceivePortImpl._handleMessage
You can test the error live at this URL http://web3.arista-dev.appspot.com/add/my-name
Remove doesn't seem to work either but yields no error. Here is my code:
import 'dart:io';
import 'dart:async';
import 'package:shelf/shelf.dart' as shelf;
import 'package:redstone/server.dart' as app;
import 'package:restonetest/model.dart';
import 'package:gcloud/db.dart';
import 'package:appengine/appengine.dart';
Key get itemsRoot => context.services.db.emptyKey.append (ItemRoot, id: 1);
DatastoreDB db = context.services.db;
Future<List<Item>> queryItems ()
{
var query = context.services.db.query (Item, ancestorKey: itemsRoot)
..order ('name');
return query.run ().toList ();
}
Future<List<Item>> addItemToDB (Item item)
{
return db.query(Item, ancestorKey: itemsRoot).run()
.any((i) => i.name == item.name)
.then((exists)
{
return ! exists ? db.commit(inserts: [item]) : false;
});
}
#app.Route("/")
helloWorld() => queryItems();
#app.Route('/add/:name')
addItem (String name)
{
return addItemToDB (new Item.create (name, itemsRoot)).then ((_)
{
print (name);
return helloWorld();
});
}
#app.Route('/delete/:name')
deleteItem (String name)
{
var query = db.query (Item, ancestorKey: itemsRoot)..filter('name =', name);
return query.run().toList().then((list)
{
var toDelete = list.map((i) => i.key).toList();
return db.commit(deletes: toDelete);
})
.then((_) => helloWorld());
}
main() {
app.setupConsoleLog();
app.setUp();
runAppEngine(app.handleRequest);
//app.start();
}
At the moment, package:appengine only allows one to call API services inside a request handler:
Each request handler invocation will get a new set of services. This allows package:appengine to give each request handler e.g. a different logging service instance. This allows all logging API calls to be grouped by request.
The way this is achieved in dart / package:appengine is by using Zones. For every incoming request, package:appengine makes a new Zone with API services and calls the request handler inside that. The handler can then use 'context.services.' to make API calls.
So the issue in the program posted above, is that the DatastoreDB service gets cached from the first request (global fields are lazily initialized) and may no longer work for subsequent requests.
Changing
DatastoreDB db = context.services.db;
to
DatastoreDB get db => context.services.db;
should fix the problem, since the services object will be re-fetched every time from the request handler Zone.
This being said:
a) The error is swallowed reported is misleading and will be fixed in package:appengine
b) In the near future, we will allow background tasks / tasks making API calls outside of a request handler. This is missing at the moment, but will be implemented.
I hope this helps.

Resources