How to create Azure DevOps task via rest api - powershell
I wrote some PowerShell functions to help me create user stories a bit faster, and this all works great, but now I am stuck figuring out how to create Tasks for a User Story/Work Item, and obviously having them be assigned to a specific Work Item.
I also can't find any documentation describing this.
I almost imagine that I need use the uri "https://dev.azure.com/$($Organisation)/$Project/_apis/wit/workitems/`$Task?api-version=5.1" but I can't see how to associate it with a work item as part of this, or after.
Can anyone help or point me at some actual documentation for this, please?
Edit: While looking for something else, I stumbled across this, but sadly that errors out for me, so it might be deprecated by now...
Edit; Thanks for the help everyone. This now works for me
This is my code in case it becomes useful for someone some day in the future:
#96116 is the parent work item, 96113 the child task
$ContentType = "application/json-patch+json"
$Token = System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PersonalAccessToken)"))
$Header = #{Authorization = 'Basic ' + $Token;accept=$ContentType}
$uri = "https://dev.azure.com/$Organisation/$Project/_apis/wit/workitems/96113?api-version=6.1-preview.3"
$body= #'
[
{
"op": "add",
"path": "/relations/-",
"value": {
"rel": "System.LinkTypes.Hierarchy-Reverse",
"url": "https://dev.azure.com/$Organisation/$Project/_apis/wit/workitems/96113",
"attributes": {
"isLocked": false,
"name": "Parent"
}
}
}
]
'#
Invoke-RestMethod -Uri $uri -Method PATCH -Headers $Header -Body $Body -ContentType $contentType
You can follow the steps below to create a new Task, and link a specified User Story as the Parent of this new Task:
Use the endpoint "Work Items - Create" to create the new Task.
Use the endpoint "Work Items - Update" to link the specified User Story as the Parent.
Request URI
PATCH https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/{id}?api-version=6.1-preview.3
Required Header
Content-Type: application/json-patch+json
Request Body
[
{
"op": "add",
"path": "/relations/-",
"value": {
"rel": "System.LinkTypes.Hierarchy-Reverse",
// This is the URL of the linked parent work item.
"url": "https://dev.azure.com/{organization}/{project}/_apis/wit/workItems/{parent work item ID}",
"attributes": {
"isLocked": false,
"name": "Parent"
}
}
}
]
I have tested this method, it can work fine as expected.
As mentioned above, adding a relation can be done after creation with a separate PATCH request, but you can also combine multiple Work Item Tracking requests in a single call. You need to POST to the batch endpoint and send an array of JsonPatchDocuments:
PATCH https://dev.azure.com/{organization}/{project}/_apis/wit/$batch?api-version=2.2
"Content-Type": "application/json"
"Accept": "application/json"
"Authorization": "Basic {PAT}"
[
{
"method": "PATCH",
// Replace $Task below with the WIT you want to create
"uri": "/{Project}/_apis/wit/workitems/$Task?api-version=2.2",
"headers": { "Content-Type": "application/json-patch+json" },
"body": [
{ "op": "add", "path": "/fields/System.Title", "value": "Customer can sign in using their Microsoft Account" },
// Indicates new work item instead of an update. Each new work item uses a unique negative number in the batch.
{ "op": "add", "path": "/id", "value": "-1" },
{
"op": "add",
"path": "/relations/-",
"value": {
"rel": "System.LinkTypes.Hierarchy-Reverse",
"url": "https://dev.azure.com/{organization}/{project}_apis/wit/workitems/{work item id to link to}"
}
}
]
}
]
With this API you can also create a tree of work items in a single call. You use the negative IDs to link workitems together and they ge translated to their real work item ids as the batch is executed.
Docsr for the batch API are here:
Work Item Tracking - Batch REST API
It looks like Microsoft has documentation for creating, deleting, and updating work items.
Your guess was close. Here's the provided example:
Request
POST https://dev.azure.com/fabrikam/{project}/_apis/wit/workitems/${type}?api-version=6.1-preview.3
Body
[
{
"op": "add",
"path": "/fields/System.Title",
"from": null,
"value": "Sample task"
}
]
Response
{
"id": 131489,
"rev": 1,
"fields": {
"System.AreaPath": "CustomProcessPrj",
"System.TeamProject": "CustomProcessPrj",
"System.IterationPath": "CustomProcessPrj",
"System.WorkItemType": "Task",
"System.State": "New",
"System.Reason": "New",
"System.CreatedDate": "2017-10-06T01:04:51.57Z",
"System.CreatedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"uniqueName": "fabrikamfiber4#hotmail.com",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"descriptor": "aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
},
"System.ChangedDate": "2017-10-06T01:04:51.57Z",
"System.ChangedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"uniqueName": "fabrikamfiber4#hotmail.com",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=d291b0c4-a05c-4ea6-8df1-4b41d5f39eff",
"descriptor": "aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
},
"System.Title": "Sample task",
"Microsoft.VSTS.Common.StateChangeDate": "2017-10-06T01:04:51.57Z",
"Microsoft.VSTS.Common.Priority": 2
},
"_links": {
"self": {
"href": "https://dev.azure.com/fabrikam/_apis/wit/workItems/131489"
},
"workItemUpdates": {
"href": "https://dev.azure.com/fabrikam/_apis/wit/workItems/131489/updates"
},
"workItemRevisions": {
"href": "https://dev.azure.com/fabrikam/_apis/wit/workItems/131489/revisions"
},
"workItemHistory": {
"href": "https://dev.azure.com/fabrikam/_apis/wit/workItems/131489/history"
},
"html": {
"href": "https://dev.azure.com/fabrikam/web/wi.aspx?pcguid=20cda608-32f0-4e6e-9b7c-8def7b38d15a&id=131489"
},
"workItemType": {
"href": "https://dev.azure.com/fabrikam/aaee31d9-14cf-48b9-a92b-3f1446c13f80/_apis/wit/workItemTypes/Task"
},
"fields": {
"href": "https://dev.azure.com/fabrikam/_apis/wit/fields"
}
},
"url": "https://dev.azure.com/fabrikam/_apis/wit/workItems/131489"
}
Related
Cannot capture sandbox PayPal payment
I am currently trying the Orders API of PayPal using Postman, but cannot capture any payment. For now, I could get the access token, set it to a collection variable, then created orders using (note the access token is set in the Authorization tab): POST https://api-m.sandbox.paypal.com/v2/checkout/orders Body: { "intent": "CAPTURE", "purchase_units": [ { "amount": { "currency_code": "USD", "value": "10.00" } } ] } The request was successful with response body: { "id": "<random-id>", "status": "CREATED", "links": [ { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>", "rel": "self", "method": "GET" }, { "href": "https://www.sandbox.paypal.com/checkoutnow?token=<random-id>", "rel": "approve", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>", "rel": "update", "method": "PATCH" }, { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>/capture", "rel": "capture", "method": "POST" } ] } Then I proceeded to rel:approve's link using a browser https://www.sandbox.paypal.com/checkoutnow?token=<random-id> and signed in with my sandbox account. It shows me the usual payment window but when I pressed the "Continue" button, it tried to redirect to the return page but instead, refreshed the page itself. When I tryed to check the order using rel:self's link: GET https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>. It correctly showed the sandbox account's shipping details (name and address), but the status remained CREATED (not APPROVED or COMPLETED): { "id": "<random-id>", "intent": "CAPTURE", "status": "CREATED", "purchase_units": [ { "reference_id": "default", "amount": { "currency_code": "USD", "value": "10.00" }, "payee": { "email_address": "<payee-email>", "merchant_id": "<payee-id>" }, "shipping": { "name": { "full_name": "<payer-name>" }, "address": { "address_line_1": "<payer-address-1>", "address_line_2": "<payer-address-2>", "admin_area_2": "<payer-address-3>", "admin_area_1": "<payer-address-4>", "postal_code": "<payer-address-5>", "country_code": "<payer-address-6>" } } } ], "create_time": "<time-of-post-request>", "links": [ { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>", "rel": "self", "method": "GET" }, { "href": "https://www.sandbox.paypal.com/checkoutnow?token=<random-id>", "rel": "approve", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>", "rel": "update", "method": "PATCH" }, { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>/capture", "rel": "capture", "method": "POST" } ] } When I tried to capture the payment using rel:caputure's link: POST https://api.sandbox.paypal.com/v2/checkout/orders/<random-id>/capture with header Content Type: application/json and empty body, it said "payer has not approved the Order for payment", despite I getting the shipping details from the GET request before: { "name": "UNPROCESSABLE_ENTITY", "details": [ { "issue": "ORDER_NOT_APPROVED", "description": "Payer has not yet approved the Order for payment. Please redirect the payer to the 'rel':'approve' url returned as part of the HATEOAS links within the Create Order call or provide a valid payment_source in the request." } ], "message": "The requested action could not be performed, semantically incorrect, or failed business validation.", "debug_id": "6a10ea489ffce", "links": [ { "href": "https://developer.paypal.com/docs/api/orders/v2/#error-ORDER_NOT_APPROVED", "rel": "information_link", "method": "GET" } ] } I have three questions: Was I using the Orders API correctly? Did I miss some HTTP requests and/or some crucial steps? I had the return URL set for my sandbox application, why did the payment page not redirect me but instead refreshed itself? Did I miss some setup beforehand? Why did I fail to capture the payment like above? P.S. After some digging I think I might be missing the authorize payment step but I have no idea how to do it. (Client-side request? Server-side request?)
I proceeded to rel:approve's link .. when I pressed the "Continue" button, it tried to redirect to the return page but instead, refreshed the page itself. You did not specify a return_url , so there is nowhere to return to. Refreshing is all that can be done. What you should do is not redirect to an approval URL, and integrate with no redirects. For this make two routes on your server, one for 'Create Order' and one for 'Capture Order', documented here. These routes should return only JSON data (no HTML or text). The latter one should (on success) store the payment details in your database before it does the return (particularly purchase_units[0].payments.captures[0].id, the PayPal transaction ID) Pair those two routes with the following approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server
I was also having trouble with this issue, I solved it by expanding the request body, much like #preston-phx said, with the return URL, and it looked something like this: { "intent": "CAPTURE", "payer": { "email_address": requestBody.payer_email }, "purchase_units": [{ "amount": { "currency_code": "USD", "value": requestBody.amount }, "payee": { "email_address": requestBody.payee_email }, "payment_instruction": { "disbursement_mode": "INSTANT", // can be INSTANT or DELAYED "platform_fees": [ { "amount": { "currency_code": "USD", "value": calculateFeesFromAmount(requestBody.amount) } } ] } }], "redirect_urls": { "return_url": "https://example.com/paypalpay/order/approved", "cancel_url": "https://example.com/paypalpay/order/cancelled" }, "application_context": { "brand_name": "Header for payment page", "locale": "en-US", "landing_page": "BILLING", // can be NO_PREFERENCE, LOGIN, BILLING "shipping_preference": "NO_SHIPPING" // because I didn't want shipping info on the page, "user_action": "PAY_NOW", // Button name, can be PAY_NOW or CONTINUE "return_url": "https://example.com/paypalpay/order/approved", "cancel_url": "https://example.com/paypalpay/order/cancelled" } } This also helped me customise the payment page to an extent. I hope Paypal folks include these in the docs at the correct places, most of devs have to dig through a lot of documentation to create an extensive, usable request body.
WireMock set optional Parameters possibly?
i am very new here. I look up to setup die optional parameters in my Pattern. I have already read the documentary WireMock, but I have not found anything suitable. My question is, can I query the parameters in any order =? The next one would is ,y caseInsensitive doesn't work. I dont know why. { "priority": 1, "request": { "method": "GET", "headers": { "Content-Type": { "equalTo": "application/json", "caseInsensitive": true } }, "urlPattern": "/example\\?name=([a-zA-Z0-9]*)&id=([a-zA-Z0-9]*)" }, "response": { "status": 200, "bodyFileName": "example/test.json" } }
As you've written your urlPattern, the query parameter matching is not order indifferent. If you want the query parameters to indifferent, you'd need to do something like... { "priority": 1, "request": { "method": "GET", "headers": { "Content-Type": { "equalTo": "application/json" } }, "urlPath": "/example", "queryParameters": { "name": { "matches": "([a-zA-Z0-9]*)" }, "id": { "matches": "([a-zA-Z0-9]*)" } } }, "response": { "status": 200, "bodyFileName": "example/test.json" } }
The result of the query comes back the same answer. i want, if i call my request, that the order of Parameters doesn't matter. example Request: /example?name=max&id=01 example2 Request: /example?id=01&name=max it should be get same Response. And it should be case-insensitive.
Linkedin share/ugc post api is not providing posts
I am using the following endpoint to retrieve posts in the page https://api.linkedin.com/v2/shares?q=owners&owners=urn:li:organization:xxxxx Even though there are posts, getting empty elements array { "elements": [], "paging": { "total": 1, "count": 10, "start": 0, "links": [ { "rel": "next", "href": "/v2/shares?count=10&owners=urn:li:organization:XXXXXXX&q=owners&start=0", "type": "application/json" } ] } }
Using the right credentials worked
Fetching multiple event details in Office 365
I need to fetch information related to eventId from office 365 for multiple events. Is there a way I can get that info in a single REST call? I want specific events only (based on eventId's only)
A batch request may be what you are looking for. See json Batching Documentation for more information Keep in mind that batching is currently limited to 20 requests per message (known issues) Example: You will need to send a POST Message to the batch endpoint https://graph.microsoft.com/v1.0/$batch inside the body you will need to include your requests: Note: do not include the server url (https://graph.microsoft.com/v1.0/) in the url property or the request will fail with "BadRequest - Invalid request Uri". Request-Body: { "requests": [ { "id": "1", "method": "GET", "url": "/me/calendarview?startdatetime=2018-03-01T18:31:34.206Z&enddatetime=2018-03-12T18:31:34.206Z" }, { "id": "2", "method": "GET", "url": "/me/events/{someEventId}" }, ] } When the server has processed all requests an response array containing the results will be sent back: Server-Response: { "responses": [ { "id": "2", "status": 200, "headers": { "OData-Version": "4.0", "Content-Type": "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8", "ETag": "W/\"Z+ICSvkiAfZX7XWQAZ6IH==\"" }, "body": { // the event object } }, { "id": "1", "status": 200, "headers": { "OData-Version": "4.0", "Content-Type": "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8" }, "body": { "#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('aUserID')/calendarView", "value": [ // list of found event-objects ] } } ] }
Simple REST API Call from logic app - Azure
First of all I want you to know that I am new to Azure. Recently, I am trying to work on Azure Logic App. My motive is to make a simple REST API Call from the HTTP API (from Microsoft) and mail the response JSON via Office 365 connector. Here is my code: { .. . . "triggers": { "http": { "recurrence": { "frequency": "Day", "interval": 1 }, "type": "Http", "inputs": { "method": "POST", "headers": { "Content-Type": "application/json" }, "uri": "http://xxx/wcf/myrestservice.svc/is_online" } } }, "actions": { "office365connector": { "type": "ApiApp", "inputs": { "apiVersion": "2015-01-14", "host": { "id": "/subscriptions/xxx/resourcegroups/resourcegroup1/providers/Microsoft.AppService/apiapps/office365connector", "gateway": "https://xxx.azurewebsites.net" }, "operation": "SendMail", "parameters": { "message": { "To": "xxx#example.com", "Subject": "My Service Status", "Importance": "High", "Body": "Hi #{triggers().outputs.body.Is_OnlineResult}" } }, "authentication": { "type": "Raw", "scheme": "Zumo", "parameter": "#parameters('/subscriptions/xxx/resourcegroups/resourcegroup1/providers/Microsoft.AppService/apiapps/office365connector/token')" } }, "conditions": [] } }, "outputs": {} } I am wondering, how could I get the response of the HTTP call? Then I want to send the same in the mail body. Please correct me if I am going in wrong direction. Any response from you will be very helpful to me.
Manish! Have you tried using "Content"? #triggers().outputs.body.Content