I have working code to add a tab to the "General" channel of every team created. The users want the tab to be the first (default) of the "General" channel
Is there a way to do it?
var channels = await graphClient.Teams[groupID].Channels.Request().Filter("displayName eq 'General'").GetAsync();
var teamsTab = new TeamsTab
{
DisplayName = "Project KickOff",
Configuration = new TeamsTabConfiguration
{
ContentUrl = $"https://{domain}/main.aspx?id={project.ProjectKickOff}",
WebsiteUrl = $"https://{domain}/main.aspx?id={project.ProjectKickOff}"
},
AdditionalData = new Dictionary<string, object>()
{
{"teamsApp#odata.bind", "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.web"}
}
};
TeamsTab tab = await graphClient.Teams[groupID].Channels[channels[0].Id].Tabs.Request().AddAsync(teamsTab);
Thank you
There doesn't seem to be any way to add any sort order while adding tab.
Related
Clerify,
By get I mean to generate or get the number I need to provide, NOT the number already exists in the test results.
When creating tests result with steps information (manual run), need to provide value for a field called actionPath
https://learn.microsoft.com/en-us/rest/api/azure/devops/test/action%20results/list?view=azure-devops-rest-6.0#testactionresultmodel
Is there a way to find or generate this value so I can create tests results using the API? It seems there is no consistent or clear way of what or where.
After some try/error I have found the solution.
Phase One - Get the Step ID
You need to install HTML Agility Pack nuget to be able to extract data from the HTML.
When creating a test step, it will have an automatic ID which is represented in it's HTML. In order to get the test steps HTML and extract the IDs, need to use the following code (or similar):
// create a client (assuming you know how to create vss connection)
var client = vssConnction.GetClient<WorkItemTrackingHttpClient>();
// get the work item with all fields
var item = client.GetWorkItemAsync(<item id>, expand: WorkItemExpand.All).GetAwaiter().GetResult();
// get the HTML
var html = $"{item.Fields["Microsoft.VSTS.TCM.Steps"]}"
// load the HTML into DOM object
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
// extract all IDs. The actionPath is the hex form of the step ID (.ToString("x"))
var ids = htmlDocument.DocumentNode.SelectNodes("//step").Select(i => int.Parse(i.GetAttributeValue("id", "0")).ToString("x"));
Phase Two - Get the Action Path
The actionPath is the hex form of the step ID in 8 digitis with leading zeros (e.g. id 10 will be 0000000a). In order to parse the id into action path use the following code (or similar):
var actionPath = ids.Select(i => new string('0', 8 - i.Length) + i);
Now you can tell the actionPath when creating an action test result
Full Code Workflow (was not validated for errors)
// credentials
var basicCredential = new VssBasicCredential("", personalAccessToken);
var credentials = new VssCredentials(basicCredential);
// connection
var connection = new VssConnection(new Uri("your collection URI"), credentials);
// clients
var testManagement = connection.GetClient<TestManagementHttpClient>();
// test points
var pointsFilter = new PointsFilter { TestcaseIds = new[] { <test_id>, <test_id>, ... } };
var pointsQuery = new TestPointsQuery() { PointsFilter = pointsFilter };
var points = testManagement.GetPointsByQueryAsync(query, project).GetAwaiter().GetResult().Points;
// test run
var runCreateModel = new RunCreateModel(name: "My Test Run", pointIds: points, plan: new ShallowReference(id: $"{<test_plan_id>}"));
var testRun = testManagement.CreateTestRunAsync(runCreateModel, "<project name>").GetAwaiter().GetResult();
// iteration
var dateTime = DateTime.Now;
var date = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond, dateTime.Kind);
var iteration = new TestIterationDetailsModel
{
Id = 1,
StartedDate = date,
CompletedDate = date.AddMinutes(5),
Comment = "My Test Iteration"
}
// action
var actionResult = new TestActionResultModel
{
ActionPath = "<actionPath>", // the one we extracted from the HTML
StepIdentifier = "<the_test_step_id>", // the one we extracted from the HTML
IterationId = <the_iteration_id>,
StartedDate = date,
Outcome = TestOutcome.Passed,
CompletedDate = date.AddMinutes(5)
};
iteration.ActionResults = new List<TestActionResultModel> { actionResult };
// test result
var testCaseResult = testManagement
.GetTestResultByIdAsync("<project name>", testRun.Id, <test_results_id>, ResultDetails.Iterations)
.GetAwaiter()
.GetResult()
.First();
testCaseResult.IterationDetails.Add(iteration);
testManagement.UpdateTestResultsAsync(new[] { testCaseResult }, "<project name>", testRun.Id).GetAwaiter().GetResult();
How to create or get the value of actionPath
You can use the below rest api to get the value of actionPath.
https://dev.azure.com/{organization}/{project}/_apis/test/Runs/{runId}/results/{testCaseResultId}?detailsToInclude=iterations&api-version=6.0
Test in Postman:
I am using GraphAPI SDK to create a new Team in Microsoft Teams:
var newTeam = new Team()
{
DisplayName = teamName,
Description = teamName,
AdditionalData = new Dictionary<string, object>()
{
{"template#odata.bind", "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"}
},
Members = new TeamMembersCollectionPage()
{
new AadUserConversationMember
{
Roles = new List<String>()
{
"owner"
},
AdditionalData = new Dictionary<string, object>()
{
{"user#odata.bind", $"https://graph.microsoft.com/v1.0/users/{userId}"}
}
}
}
};
var team = await this.graphStableClient.Teams
.Request()
.AddAsync(newTeam);
The problem is that I get always null. According documentation this method returns a 202 response (teamsAsyncOperation), but the AddAsync method from SDK returns a Team object. Is there any way to get the tracking url to check if the team creation has been finished with the SDK?
Documentation and working SDK works different... As they wrote in microsoft-graph-docs/issues/10840, we can only get the teamsAsyncOperation header values if we use HttpRequestMessage as in contoso-airlines-teams-sample. They wrote to the people who asks this problem, look to the joined teams :)) :)
var newTeam = new Team()
{
DisplayName = model.DisplayName,
Description = model.Description,
AdditionalData = new Dictionary<string, object>
{
["template#odata.bind"] = $"{graph.BaseUrl}/teamsTemplates('standard')",
["members"] = owners.ToArray()
}
};
// we cannot use 'await client.Teams.Request().AddAsync(newTeam)'
// as we do NOT get the team ID back (object is always null) :(
BaseRequest request = (BaseRequest)graph.Teams.Request();
request.ContentType = "application/json";
request.Method = "POST";
string location;
using (HttpResponseMessage response = await request.SendRequestAsync(newTeam, CancellationToken.None))
location = response.Headers.Location.ToString();
// looks like: /teams('7070b1fd-1f14-4a06-8617-254724d63cde')/operations('c7c34e52-7ebf-4038-b306-f5af2d9891ac')
// but is documented as: /teams/7070b1fd-1f14-4a06-8617-254724d63cde/operations/c7c34e52-7ebf-4038-b306-f5af2d9891ac
// -> this split supports both of them
string[] locationParts = location.Split(new[] { '\'', '/', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
string teamId = locationParts[1];
string operationId = locationParts[3];
// before querying the first time we must wait some secs, else we get a 404
int delayInMilliseconds = 5_000;
while (true)
{
await Task.Delay(delayInMilliseconds);
// lets see how far the teams creation process is
TeamsAsyncOperation operation = await graph.Teams[teamId].Operations[operationId].Request().GetAsync();
if (operation.Status == TeamsAsyncOperationStatus.Succeeded)
break;
if (operation.Status == TeamsAsyncOperationStatus.Failed)
throw new Exception($"Failed to create team '{newTeam.DisplayName}': {operation.Error.Message} ({operation.Error.Code})");
// according to the docs, we should wait > 30 secs between calls
// https://learn.microsoft.com/en-us/graph/api/resources/teamsasyncoperation?view=graph-rest-1.0
delayInMilliseconds = 30_000;
}
// finally, do something with your team...
I found a solution from another question... Tried and saw that it's working...
During adding site links to campaign feed, I would like to set device preference for mobile to be checked.
How can I do it with ad words API ?
platformOperand.stringValue = "Mobile";//it also can be set for "Desktop"
I tried to do it like this:
ConstantOperand platformOperand = new ConstantOperand();
platformOperand.stringValue = "Mobile";//it also can be set for "Desktop"
platformOperand.type = ConstantOperandConstantType.STRING;
Function platformFunction = new Function();
platformFunction.lhsOperand = new FunctionArgumentOperand[] {
platformRequestContextOperand };
platformFunction.#operator = FunctionOperator.EQUALS;
platformFunction.rhsOperand = new FunctionArgumentOperand[] {
platformOperand };
and then it combined in function with links ids from google service and mutated for campaign feed service.
No exception and link added but when I enter to link edit, "Mobile" option remain unmarked.
Please advise.
I found the answer:
you shuld set devicePreference when you set propertes for FeedItem:
// Create the feed item and operation.
var item = new FeedItem();
item.feedId = siteLinksData.SiteLinksFeedId;
item.attributeValues = new FeedItemAttributeValue[]
{linkTextAttributeValue, linkUrlAttributeValue };
if (value.DeviceType == Device.Mobile)
{
item.devicePreference = new FeedItemDevicePreference();
item.devicePreference.devicePreference = 30001L;
item.devicePreference.devicePreferenceSpecified = true;
}
and this is most important part:
item.devicePreference = new FeedItemDevicePreference();
item.devicePreference.devicePreference = 30001L;
item.devicePreference.devicePreferenceSpecified = true;
if you wondering what the meaning of 30001L, it's device criteria IDs according to adwords API
See https://developers.google.com/adwords/api/docs/appendix/platforms
The code seems to run. I don't get any error messages, but an invoice does not appear in QB after I sync. The code is basically this (http://pastebin.com/y7QENxeX) with a few (presumably) minor changes as noted. I'm able to create Accounts and Customers so I believe the basic infrastructure of my app is good. I don't understand why I'm stuck on invoices. I think my customerID is 2. I only have 5 in my company right now. And I think my itemID is 1 as I only have one in QB right now.
Any and all help is greatly appreciated.
Intuit.Ipp.Data.Qbd.PhysicalAddress physicalAddress = new Intuit.Ipp.Data.Qbd.PhysicalAddress();
physicalAddress.Line1 = "123 Main St.";
physicalAddress.Line2 = "Apt. 12";
physicalAddress.City = "Mountain View";
physicalAddress.CountrySubDivisionCode = "CA";
physicalAddress.Country = "USA";
physicalAddress.PostalCode = "94043";
physicalAddress.Tag = new string[] { "Billing" };
Intuit.Ipp.Data.Qbd.InvoiceHeader invoiceHeader = new Intuit.Ipp.Data.Qbd.InvoiceHeader();
invoiceHeader.ARAccountId = new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "37" };
invoiceHeader.ARAccountName = "Accounts Receivable";
// original code : invoiceHeader.CustomerId = new IdType() { idDomain = idDomainEnum.NG, Value = "3291253" };
invoiceHeader.CustomerId = new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "2" };
invoiceHeader.Balance = (decimal)100.00;
invoiceHeader.BillAddr = physicalAddress;
invoiceHeader.BillEmail = "detroit#tigers.com";
invoiceHeader.CustomerName = "Detroit Tigers";
invoiceHeader.DocNumber = "1234567";
invoiceHeader.DueDate = DateTime.Now;
invoiceHeader.ShipAddr = physicalAddress;
invoiceHeader.ShipDate = DateTime.Now;
invoiceHeader.TaxAmt = (decimal)5;
invoiceHeader.TaxRate = (decimal).05;
invoiceHeader.ToBeEmailed = false;
invoiceHeader.TotalAmt = (decimal)105.00;
List<Intuit.Ipp.Data.Qbd.InvoiceLine> listLine = new List<Intuit.Ipp.Data.Qbd.InvoiceLine>();
//Loop for multiple invoice lines could be added here
Intuit.Ipp.Data.Qbd.ItemsChoiceType2[] invoiceItemAttributes = { Intuit.Ipp.Data.Qbd.ItemsChoiceType2.ItemId, Intuit.Ipp.Data.Qbd.ItemsChoiceType2.UnitPrice, Intuit.Ipp.Data.Qbd.ItemsChoiceType2.Qty };
// original code : object[] invoiceItemValues = { new IdType() { idDomain = idDomainEnum.QB, Value = "5" }, new decimal(33), new decimal(2) };
object[] invoiceItemValues = { new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "1" }, new decimal(33), new decimal(2) };
var invoiceLine = new Intuit.Ipp.Data.Qbd.InvoiceLine();
invoiceLine.Amount = 66;
invoiceLine.AmountSpecified = true;
invoiceLine.Desc = "test " + DateTime.Now.ToShortDateString();
invoiceLine.ItemsElementName = invoiceItemAttributes;
invoiceLine.Items = invoiceItemValues;
invoiceLine.ServiceDate = DateTime.Now;
invoiceLine.ServiceDateSpecified = true;
listLine.Add(invoiceLine);
Intuit.Ipp.Data.Qbd.Invoice invoice = new Intuit.Ipp.Data.Qbd.Invoice();
invoice.Header = invoiceHeader;
invoice.Line = listLine.ToArray();
Intuit.Ipp.Data.Qbd.Invoice addedInvoice = commonService.Add(invoice);
Chris
You need to read the following information about how QuickBooks for Windows Sync Manager works, how to see if Sync ran correctly, if objects are in an errored state and how to resolve. It could be any number of things. Once a record is inserted into the cloud, it asynchronously downloads to QuickBooks on the desktop, at which time business logic is applied and records are matched from the cloud to the desktop. If there is an issue, sync manager will show a record of the object that failed, why it failed and the object will now be in an error state.
At this point you can review the error and take steps to fix, like revert or update and resubmit. Links to the documentation below.
QuickBooks Sync Manager
Data Sync
Objects in Errored State
Sync Activity
Sync Status
regards
Jarred
I want to retrieve all clientID from my MCC account. I'm using this code
AdWordsUser user = new AdWordsUser(adwordsPropertyService.getEmail(), adwordsPropertyService.getPassword(),
null, adwordsPropertyService.getUseragent(), adwordsPropertyService.getDeveloperToken(),
adwordsPropertyService.getUseSandbox());
InfoServiceInterface infoService = user.getService(AdWordsService.V201109.INFO_SERVICE);
InfoSelector selector = new InfoSelector();
selector.setApiUsageType(ApiUsageType.UNIT_COUNT_FOR_CLIENTS);
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
selector.setDateRange(new DateRange(today, today));
selector.setIncludeSubAccounts(true);
ApiUsageInfo apiUsageInfo = infoService.get(selector);
for (ApiUsageRecord record : apiUsageInfo.getApiUsageRecords()) {
......
But apiUsageInfo.getApiUsageRecords return my only some clientId.
Have you any suggests?
My Answer will be helpful for PHP Developers
I am using v201502(php), You will get all account details from ManagedCustomerService api. Please refer the following URL https://developers.google.com/adwords/api/docs/reference/v201502/ManagedCustomerService
This is the sample code i used,
function DisplayAccountTree($account, $link, $accounts, $links, $depth) {
print str_repeat('-', $depth * 2);
printf("%s, %s\n", $account->customerId, $account->name);
if (array_key_exists($account->customerId, $links)) {
foreach ($links[$account->customerId] as $childLink) {
$childAccount = $accounts[$childLink->clientCustomerId];
DisplayAccountTree($childAccount, $childLink, $accounts, $links,
$depth +1);
}
}
}
function GetAccountHierarchyExample(AdWordsUser $user) {
// Get the service, which loads the required classes.
$user->SetClientCustomerId('xxx-xxx-xxxx');
$managedCustomerService =
$user->GetService('ManagedCustomerService');
// Create selector.
$selector = new Selector();
// Specify the fields to retrieve.
$selector->fields = array('CustomerId', 'Name');
// Make the get request.
$graph = $managedCustomerService->get($selector);
// Display serviced account graph.
if (isset($graph->entries)) {
// Create map from customerId to parent and child links.
$childLinks = array();
$parentLinks = array();
if (isset($graph->links)) {
foreach ($graph->links as $link) {
$childLinks[$link->managerCustomerId][] = $link;
$parentLinks[$link->clientCustomerId][] = $link;
}
}
// Create map from customerID to account, and find root account.
$accounts = array();
$rootAccount = NULL;
foreach ($graph->entries as $account) {
$accounts[$account->customerId] = $account;
if (!array_key_exists($account->customerId, $parentLinks)) {
$rootAccount = $account;
}
}
// The root account may not be returned in the sandbox.
if (!isset($rootAccount)) {
$rootAccount = new Account();
$rootAccount->customerId = 0;
}
// Display account tree.
print "(Customer Id, Account Name)\n";
DisplayAccountTree($rootAccount, NULL, $accounts, $childLinks, 0);
} else {
print "No serviced accounts were found.\n";
}
}
GetAccountHierarchyExample($user);
SetClientCustomerId will be the parent ID of your all accounts, It will be appeared near the Sign Out button of you google AdWords account, Please see the attached image
I hope this answer will be helpful, Please add your comments below if you want any further help
If you need just the list of clientCustomerIds, try ServicedAccountService.
Here is a code example that shows how this may be done.
Next time, you might also want to consider asking the question on the official forum for AdWords API: https://groups.google.com/forum/?fromgroups#!forum/adwords-api