Graph API: BadGateway when trying to create a Team - microsoft-graph-api

I'm using Graph API v1.0 with application permissions. I'm able to create a Group, after creating the Group I try to create a Team. The API call always fails with:
{
"error": {
"innerError": {
"date": "2020-02-22T00:58:53",
"request-id": "ca92dfa7-283f-49e4-af47-b46f6d8ad53f"
},
"message": "Failed to execute backend request.",
"code": "BadGateway"
}
}
The body of my request is:
{
"memberSettings": {
"allowCreateUpdateChannels": False,
},
"messagingSettings": {
"allowUserEditMessages": True,
"allowUserDeleteMessages": True,
},
"funSettings": {
"allowGiphy": False,
"giphyContentRating": "strict"
},
"discoverySettings": {
"showInTeamsSearchAndSuggestions": False
}
}
I've read the docs and retried up to 3 times with a 10 second delay. I've even inserted a 15 minute delay after creating the group to confirm that it is properly created and propagated. Neither of those changes has had any effect.
I've also tried using the new Beta API version of Create Team, and I get the same BadGateway error.
What am I doing wrong?

I don't know if this relates anymore, but I have seen the same problem before and it was because the true and false values should not (according to JSON specs) not be capitalized first letter.
False -> false and True -> true
But this may be handled in serializers and deserializers now. But check it.
There is also an extra comma behind "allowCreateUpdateChannels" and "allowUserDeleteMessages" that necessarily doesn't need to be there.

It is happening the same to me, but I am using C# (Assembly Microsoft.Graph, Version=3.3.0.0)
var team = new Team
{
MemberSettings = new TeamMemberSettings
{
AllowCreateUpdateChannels = true
},
MessagingSettings = new TeamMessagingSettings
{
AllowUserEditMessages = true,
AllowUserDeleteMessages = true
},
FunSettings = new TeamFunSettings
{
AllowGiphy = true,
GiphyContentRating = GiphyRatingType.Strict
}
};
var res = await graphClient.Groups[groupid].Team.Request().PutAsync(te

I'm using Graph API SDK 3.1.0. Try setting ODataType = null within the team objects
var team = new GraphApi.Team
{
MemberSettings = new GraphApi.TeamMemberSettings
{
AllowCreateUpdateChannels = true,
ODataType = null
},
MessagingSettings = new GraphApi.TeamMessagingSettings
{
AllowUserEditMessages = true,
AllowUserDeleteMessages = true,
ODataType = null
},
FunSettings = new GraphApi.TeamFunSettings
{
AllowGiphy = true,
GiphyContentRating = GraphApi.GiphyRatingType.Strict,
ODataType = null
},
ODataType = null
};

Related

cdk watch command forces full deploy with unrelated error on file change

I'm developing a little CDKv2 script to instantiate a few AWS services.
I have some lambda code deployed in the lambda/ folder and the frontend stored in a bucket populated using the frontend/ folder in the source.
I've noticed that whenever I make a change to any of the file inside these two, cdk watch return the following error and falls back to perform a full redeploy (which is significantly slow).
Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: Parameter or resource 'DomainPrefix' could not be found for evaluation
Falling back to doing a full deployment
Is there any way to make changes in these folders only trigger updating the related bucket content or the related lambda?
Following here the stack.ts for quick reference, just in case here you can take a look at the repo.
export class CdkAuthWebappStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const domainPrefixParam = new CfnParameter(this, 'DomainPrefix', {
type: 'String',
description: 'You have to set it in google cloud as well', //(TODO: add link to explain properly)
default: process.env.DOMAIN_NAME || ''
})
const googleClientIdParam = new CfnParameter(this, 'GoogleClientId', {
type: 'String',
description: 'From google project',
noEcho: true,
default: process.env.GOOGLE_CLIENT_ID || ''
})
const googleClientSecretParam = new CfnParameter(this, 'GoogleClientSecret', {
type: 'String',
description: 'From google project',
noEcho: true,
default: process.env.GOOGLE_CLIENT_SECRET || ''
})
if(!domainPrefixParam.value || !googleClientIdParam.value || !googleClientSecretParam.value){
throw new Error('Make sure you initialized DomainPrefix, GoogleClientId and GoogleClientSecret in the stack parameters')
}
const s3frontend = new s3.Bucket(this, 'Bucket', {
bucketName: domainPrefixParam.valueAsString+'-frontend-bucket',
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
versioned: false,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
websiteIndexDocument: "index.html",
});
//TODO: fare in modo che questa origin access identity non sia legacy quando deployo
const cfdistributionoriginaccessidentity = new cloudfront.OriginAccessIdentity(this, 'CFOriginAccessIdentity', {
comment: "Used to give bucket read to cloudfront"
})
const cfdistribution = new cloudfront.CloudFrontWebDistribution(this, 'CFDistributionFrontend', {
originConfigs: [
{
s3OriginSource: {
s3BucketSource: s3frontend,
originAccessIdentity: cfdistributionoriginaccessidentity
},
behaviors: [{
isDefaultBehavior: true,
allowedMethods: cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
forwardedValues: {
queryString: true,
cookies: { forward: 'all' }
},
minTtl: cdk.Duration.seconds(0),
defaultTtl: cdk.Duration.seconds(3600),
maxTtl: cdk.Duration.seconds(86400)
}]
}
]
})
s3frontend.grantRead(cfdistributionoriginaccessidentity)
const cfdistributionpolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['cloudfront:CreateInvalidation'],
resources: [`"arn:aws:cloudfront::${this.account}:distribution/${cfdistribution.distributionId}"`]
});
const userpool = new cognito.UserPool(this, 'WebAppUserPool', {
userPoolName: 'web-app-user-pool',
selfSignUpEnabled: false
})
const userpoolidentityprovidergoogle = new cognito.UserPoolIdentityProviderGoogle(this, 'WebAppUserPoolIdentityGoogle', {
clientId: googleClientIdParam.valueAsString,
clientSecret: googleClientSecretParam.valueAsString,
userPool: userpool,
attributeMapping: {
email: cognito.ProviderAttribute.GOOGLE_EMAIL
},
scopes: [ 'email' ]
})
// this is used to make the hostedui reachable
userpool.addDomain('Domain', {
cognitoDomain: {
domainPrefix: domainPrefixParam.valueAsString
}
})
const CLOUDFRONT_PUBLIC_URL = `https://${cfdistribution.distributionDomainName}/`
const client = userpool.addClient('Client', {
oAuth: {
flows: {
authorizationCodeGrant: true
},
callbackUrls: [
CLOUDFRONT_PUBLIC_URL
],
logoutUrls: [
CLOUDFRONT_PUBLIC_URL
],
scopes: [
cognito.OAuthScope.EMAIL,
cognito.OAuthScope.OPENID,
cognito.OAuthScope.PHONE
]
},
supportedIdentityProviders: [
cognito.UserPoolClientIdentityProvider.GOOGLE
]
})
client.node.addDependency(userpoolidentityprovidergoogle)
// defines an AWS Lambda resource
const securedlambda = new lambda.Function(this, 'AuhtorizedRequestsHandler', {
runtime: lambda.Runtime.NODEJS_14_X,
code: lambda.Code.fromAsset('lambda'),
handler: 'secured.handler'
});
const lambdaapiintegration = new apigw.LambdaIntegration(securedlambda)
const backendapigw = new apigw.RestApi(this, 'AuthorizedRequestAPI', {
restApiName: domainPrefixParam.valueAsString,
defaultCorsPreflightOptions: {
"allowOrigins": apigw.Cors.ALL_ORIGINS,
"allowMethods": apigw.Cors.ALL_METHODS,
}
})
const backendapiauthorizer = new apigw.CognitoUserPoolsAuthorizer(this, 'BackendAPIAuthorizer', {
cognitoUserPools: [userpool]
})
const authorizedresource = backendapigw.root.addMethod('GET', lambdaapiintegration, {
authorizer: backendapiauthorizer,
authorizationType: apigw.AuthorizationType.COGNITO
})
const s3deploymentfrontend = new s3deployment.BucketDeployment(this, 'DeployFrontEnd', {
sources: [
s3deployment.Source.asset('./frontend'),
s3deployment.Source.data('constants.js', `const constants = {domainPrefix:'${domainPrefixParam.valueAsString}', region:'${this.region}', cognito_client_id:'${client.userPoolClientId}', apigw_id:'${backendapigw.restApiId}'}`)
],
destinationBucket: s3frontend,
distribution: cfdistribution
})
new cdk.CfnOutput(this, 'YourPublicCloudFrontURL', {
value: CLOUDFRONT_PUBLIC_URL,
description: 'Navigate to the URL to access your deployed application'
})
}
}
Recording the solution from the comments:
Cause:
cdk watch apparently does not work with template parameters. I guess this is because the default --hotswap option bypasses CloudFormation and deploys instead via SDK commands.
Solution:
Remove the CfnParamters from the template. CDK recommends not using parameters in any case.
Perhaps cdk watch --no-hotswap would also work?

Client is getting "Forbidden" on UserInfo endpoint

So we've set up an IDP (IdentityServer4, Core2) and have been using it for our own applications without problems (Implicit Flow). Now though, one of our partners will be using our IDP to make API requests from another application.
We've setup the ApiResources:
new ApiResource("api", "API",
new List<string>() {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Profile,
"role",
"team"
})
The client in question:
new Client {
ClientName= "ClientName",
Description = "ClientDescription",
LogoUri = "/img/ClientLogos/clientLogo.png",
ClientUri = "https://client.url",
RedirectUris = {
"https://...",
"https://...",
"http://...",
"http://..."
},
AllowedGrantTypes = GrantTypes.Code,
RequireConsent = true,
ClientId = "clientId",
AllowedScopes = { "api" },
ClientSecrets = { new Secret("clientSecret".Sha256()) },
AlwaysSendClientClaims = true,
AllowOfflineAccess = true,
Claims = {
...
}
}
I (wrongfully) assumed that since the client has the "api" scope, which in turn has the "OpenID" and "Profile" scope, the client would automatically gain authorization to use the UserInfo endpoint, but they are getting the "Forbidden" StatusCode.
Can someone explain to me what we're doing wrong here?
I think you need to include IdentityResources as well, because it dictates whats part of the ID-token and what is available from the UserInfo endpoint.

Post method with multiple parameter

I am unable to insert multiple rows in database using Post method in MVC web API. I have written code for it but when i am testing by inserting multiple rows through postman it is giving error. At line first the variable "delegatetable" shows null due to which error is coming. i am not doing database connection through entity framework, i have created a DelegateTable class.
public HttpResponseMessage Post(List<DelegateTable> delegatetable)
{
try
{
using (var delegateContext = new ShowContext())
{
foreach (DelegateTable item in delegatetable)
{
DelegateTable delegates = new DelegateTable();
delegates.Salutation__c = item.Salutation__c;
delegates.First_Name__c = item.First_Name__c;
delegates.Last_Name__c = item.Last_Name__c;
delegates.Account_Name__c = item.Account_Name__c;
delegates.Contact_Email__c = item.Contact_Email__c;
delegates.Category__c = item.Category__c;
delegates.Conference_Type__c = item.Conference_Type__c;
delegates.Conference_Selection__c = item.Conference_Selection__c;
delegates.Payment_Statuss__c = item.Payment_Statuss__c;
delegates.Barcode__c = item.Barcode__c;
delegateContext.SaveChanges();
}
var message = Request.CreateResponse(HttpStatusCode.Created, delegatetable);
message.Headers.Location = new Uri(Request.RequestUri.ToString());
return message;
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
Json data that i am passing is below
[
{
"attributes": {
"type": "Registration__c",
"url": "/services/data/v43.0/sobjects/Registration__c/a3h8E0000009VuVQAU"
},
"Salutation__c": "Dr.",
"First_Name__c": "Test",
"Last_Name__c": "Test",
"Account_Name__c": "Test",
"Contact_Email__c": "test123#gmail.com",
"Category__c": "Test",
"Conference_Type__c": null,
"Conference_Selection__c": null,
"Payment_Statuss__c": null,
"Barcode__c": "Test"
},
{
"attributes": {
"type": "Registration__c",
"url": "/services/data/v43.0/sobjects/Registration__c/a3hD0000001kEfOIAU"
},
"Salutation__c": "Mr.",
"First_Name__c": "Demo",
"Last_Name__c": "Demo",
"Account_Name__c": "Demo",
"Contact_Email__c": "Demo#gmail.com",
"Category__c": "Demo",
"Conference_Type__c": null,
"Conference_Selection__c": null,
"Payment_Statuss__c": null,
"Barcode__c": null
}
]
You may try to reformat your payload as a JSON array, as the problem might be that the payload cannot be converted to a List.
Try this:
{
"delegates" :
[
{
"attributes": ..., ...
},
{ "attributes": ..., ...
},
...
]
}

Grab test's pass/fail from TestCase via TFS Rest API

I'm trying to grab the Outcome off of a testcase in TFS, looks something like this.
and I can't seem to find a straightforward way to do it. I've tried to grab the workitem directly, query for the property with no success. I was able to use the SDK to get the data (which I'm trying to avoid)
_tfs = new TfsTeamProjectCollection(new Uri(website)) { ClientCredentials = what };
_tfs.EnsureAuthenticated();
var testService = _tfs.GetService<ITestManagementService>();
var aPoint = plan.QueryTestPoints("SELECT * FROM TestPoint WHERE TestCaseId = 10").SingleOrDefault();
console.Write(aPoint.MostRecentResultOutcome);
I have the ID for the testcase from the webhook so that's not a problem. All I want is that "MostRecentResultOutcome". Is there a way to get that data from the REST api in 1 call?
You could also use below REST API which will return a list of test points through test case ID according to your code info:
GET https://Fabrikam-Fiber-inc.VisualStudio.com/DefaultCollection/fabrikam-fiber-tfvc/_apis/test/plans/1/suites/1/points?testcaseid=39&api-version=1.0
Then will get a response with lastTestRun, lastResutl, outcome...
{
"value": [
{
"id": 1,
"url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/fabrikam-fiber-tfvc/_apis/test/Plans/1/Suites/1/Points/1",
"assignedTo": {
"id": "d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"displayName": "Jamal Hartnett"
},
"configuration": {
"id": "2",
"name": "Windows 8"
},
"lastTestRun": {
"id": "28"
},
"lastResult": {
"id": "100000"
},
"outcome": "Passed",
"state": "Completed",
"testCase": {
"id": "39",
"url": null,
"webUrl": null
},
"workItemProperties": [
{
"workItem": {
"key": "Microsoft.VSTS.TCM.AutomationStatus",
"value": "Not Automated"
}
}
]
}
],
"count": 1
}
As Patrick said, you can't as of right now. What I ended up doing its grabbing the ID and System.TeamProject off of the webhook passing that along as such
private TfsTeamProjectCollection _tfs;
private ITestManagementTeamProject _project;
private readonly ITestManagementService _service;
public TfsThing(string instanceUrl, string user, string password)
{
var cred = new VssBasicCredential(user, password);
_tfs = new TfsTeamProjectCollection(new Uri(instanceUrl)) { ClientCredentials = cred };
_tfs.EnsureAuthenticated();
_service = _tfs.GetService<ITestManagementService>();
}
public string GetTestStatus(int id, string projectName)
{
var project = _service.GetTeamProject(projectName);
var result = project.TestResults.ByTestId(id);
return result.LastOrDefault()?.Outcome.ToString();
}
This was the shortest way I found -- may not be the most efficient though
Incase you were wondering, these are the two packages I used
Install-Package Microsoft.TeamFoundationServer.Client
Install-Package Microsoft.TeamFoundationServer.ExtendedClient

jqGrid: Added one extra group for grouping

I want to add grouping to my jqGrid. I have a simple model:
public class ViolationViewModel
{
[JqGridColumnFormatter(JqGridColumnPredefinedFormatters.Date, SourceFormat = "d.m.Y H:i:s", OutputFormat = "d.m.Y H:i")]
public DateTime FixationTime { get; set; }
public string OrderNumber { get; set; }
public string ViolationType { get; set; }
}
This is code in a view:
#{
var grid = new JqGridHelper<ViolationViewModel>("myGrid",
dataType: JqGridDataTypes.Json,
methodType: JqGridMethodTypes.Post,
pager: true,
sortingName: "ViolationType",
sortingOrder: JqGridSortingOrders.Asc,
url: Url.Action("Violation", "Cabinet"),
viewRecords: true,
rowsList: new List<int>() { 10, 20, 30, 50, 100 },
loadOnce: true,
multiSelect: true,
autoWidth: true,
groupingEnabled: true,
groupingView: new JqGridGroupingView { ColumnShow = new[] { false }, Fields = new[] { "ViolationType" }, DataSorted = true},
).FilterToolbar(new JqGridFilterToolbarOptions() { StringResult = true })
.Navigator(new JqGridNavigatorOptions() { Add = false, Delete = false, Edit = false, View = false, Refresh = false, Search = false });
}
I have 16 records, 15 records with the same ViolationType, 1 record have other value .
The problem with that jqGrid create three (must be two) groups, but there are two groups have the same caption. But when I click on any column (change sort) then all works fine and I have two groups.
Where is a problem?
One more question: in the rowList option I have the first value as 10. But when my grid is loaded default value is 20. How to setup it to first value?
You have set DataSorted to true. In this case the data from the initial request should be already properly sorted (by 'ViolationType') on the server side and returned in proper order.
For the second part of your question, just set rowsNumber to 10 for intial value:
#{
var grid = new JqGridHelper<ViolationViewModel>("myGrid",
dataType: JqGridDataTypes.Json,
methodType: JqGridMethodTypes.Post,
pager: true,
sortingName: "ViolationType",
sortingOrder: JqGridSortingOrders.Asc,
url: Url.Action("Violation", "Cabinet"),
viewRecords: true,
rowsList: new List<int>() { 10, 20, 30, 50, 100 },
rowsNumber: 10,
loadOnce: true,
multiSelect: true,
autoWidth: true,
groupingEnabled: true,
groupingView: new JqGridGroupingView { ColumnShow = new[] { false }, Fields = new[] { "ViolationType" }, DataSorted = true},
)
.FilterToolbar(new JqGridFilterToolbarOptions() { StringResult = true })
.Navigator(new JqGridNavigatorOptions() { Add = false, Delete = false, Edit = false, View = false, Refresh = false, Search = false });
}

Resources