How to Select multiple work items programmatically in Azure devops? - tfs

I am writing a Task Creator extension for ADO where we can create multiple tasks for multiple WITs.
Currently, I have a working extension where it allows to create multiple tasks on a single PBI.
This is how my manifest file looks
{
"manifestVersion": 1,
"id": "taskcreatortest",
"version": "1.0.24",
"name": "TaskCreator",
"description": "Create bulk tasks for a Work Item",
"publisher": "ZankhanaRana",
"galleryFlags": [
"Preview"
],
"icons": {
"default": "static/images/logo.png"
},
"scopes": [
"vso.work_write",
"vso.work",
"vso.code"
],
"targets": [
{
"id": "Microsoft.VisualStudio.Services"
}
],
"screenshots": [
{
"path": "static/images/menu.png"
},
{
"path": "static/images/createtaskform.png"
}
],
"demands": [
"api-version/3.0"
],
"tags": [
"TFS/VSTS Task Creator","Task"
],
"content": {
"details": {
"path": "overview.md"
},
"license": {
"path": "license.md"
}
},
"links": {
"getstarted": {
"uri": "https://bit.ly"
},
"support": {
"uri": "https://bit.ly"
},
"issues": {
"uri": "https://bit.ly"
}
},
"repository": {
"type": "git",
"uri": "https://bit.ly"
},
"branding": {
"color": "rgb(220, 235, 252)",
"theme": "light"
},
"files": [ ... ],
"categories": [
"Azure Test Plans"
],
"contributions": [
{
"id": "createtasks-context-menu",
"type": "ms.vss-web.action",
"description": "Toolbar item to create tasks for a work item",
"targets":[
"ms.vss-work-web.work-item-context-menu",
"ms.vss-work-web.work-item-toolbar-menu"
],
"properties": {
"uri": "static/index.html",
"text": "Create Tasks",
"title": "Create multiple tasks for a work item",
"toolbarText": "Create Tasks",
"groupId":"core"
}
},
{
"id": "createTasks-Form",
"type": "ms.vss-web.control",
"description": "Select task to create",
"targets": [ ],
"properties": {
"uri": "static/createTaskForm.html"
}
}
]
}
I am unable to select multiple work items, right-click and Create tasks.
An alert message 'Select only one item' pops up.
There are other custom extensions I installed, for example, Work Item Visualization by Microsoft Devlabs which allows multiple item selection. I think it has something to do with my configuration/manifest file.
Can someone point at what am I doing wrong?

I found the solution to this problem. Initially, I thought it has something to do with the extension configuration file (vss-extension.json); but I had a code in my script which checked for the number of items selected and if the number of items is greater than 1, it returned the alert box.
I changed that condition and everything run fine.
VSS.ready(function () {
VSS.register(VSS.getContribution().id, function (context) {
return {
execute: function (actionContext) {
if (actionContext.workItemDirty)
showDialog("Please save your work item first");
else if (actionContext.workItemIds && actionContext.workItemIds.length > 1)
showDialog("Select only one work item");
else {
var workItemType = getWorkItemType(actionContext)
if ($.inArray(workItemType, allowedWorkItemTypes) >= 0)
showPropertiesInDialog(actionContext, "Create Tasks");
else
showDialog("Not available for " + workItemType);
}
}
};
});
VSS.notifyLoadSucceeded();
});

Related

Send Jira Issue Description text as ADF table in Jira REST API

I am trying to update an issue description by sending an ADF table. My javascript code is as follows.
{
"fields" : {
"description": {
"version": 1,
"type": "doc",
"content": [
{
"type": "table",
"attrs": {
"isNumberColumnEnabled": false,
"layout": "default"
},
"content": [
{
"type": "tableRow",
"content": [
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": " Row one, cell one"
}]
}]
},
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Row one, cell two"
} ]
}]
}]
}]
}]
}
}
}
The error that I get back is "description":"Operation value must be a string". Not sure what I am doing wrong as it looks like I have the ADF formatting correct.
This is calling jira cloud via the rest api.
Found the answer. The URL must be version 3 of the API. Just have "latest" will not work. Example:
https://mycompay.atlassian.net/rest/api/3/issue/45379
and not
https://mycompany.atlassian.net/rest/api/latest/issue/45379

ARM Template for Importing Azure Key Vault Certificate in Function App

I have a function app which calls another API with a certificate. This certificate (.pfx) file is already present in the key vault. I am using below ARM template to import the certificate to SSL settings of the function app.
Note: the function app gets deployed fine when I remove section "hostNameSslStates". But after adding it, I get -
"Code": "Conflict",
"Message": "The certificate with thumbprint 'XXXXXXXX' does not match the hostname
'blobcreate-eventgridtrigger-functionapp.azurewebsites.net'."
ARM Template resources section-
`
"resources": [
//StorageAccount
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('storageAccounts_name')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[parameters('storageSKU')]",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot"
}
},
//BlobService
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2019-06-01",
"name": "[variables('blobServiceName')]",
"dependsOn": ["[variables('storageAccountResourceId')]"],
"sku": {
"name": "[parameters('storageSKU')]"//,
// "tier": "Standard"
},
"properties": {
"cors": {
"corsRules": []
},
"deleteRetentionPolicy": {
"enabled": false
}
}
},
//function app with server farm
//cert store access policies update-
{
"type": "Microsoft.KeyVault/vaults",
"name": "testARMTemplateKeyVault",
"apiVersion": "2016-10-01",
"location": "[resourceGroup().location]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "c29678d0-eceb-4df2-a225-79cf795a6b64",
"accessPolicies": [
{
"tenantId": "tenantIdOfSubscription", //obtained from Get-AzTenant
"objectId": "objectid of Microsoft Azure App Service", //obtained from Get-AzADServicePrincipal
"permissions": {
"keys": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore"
],
"secrets": [
"Get",
"List",
"Set",
"Delete",
"Recover",
"Backup",
"Restore"
],
"certificates": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"ManageContacts",
"ManageIssuers",
"GetIssuers",
"ListIssuers",
"DeleteIssuers"
],
"storage": []
}
}
],
"enabledForDeployment": false,
"enabledForDiskEncryption": false,
"enabledForTemplateDeployment": true,
"enableSoftDelete": true
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2018-02-01",
"name": "[variables('azurefunction_hostingPlanName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Y1",
"tier": "Dynamic"
},
"properties": {
"name": "[variables('azurefunction_hostingPlanName')]",
"computeMode": "Dynamic"
}
},
{
"type": "Microsoft.Web/certificates",
"name": "testingcert",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"properties": {
"keyVaultId": "[resourceId('Microsoft.KeyVault/vaults', 'testARMTemplateKeyVault')]",
"keyVaultSecretName": "testingcert",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('azurefunction_hostingPlanName'))]"
}
},
{
"apiVersion": "2018-11-01",
"type": "Microsoft.Web/sites",
"name": "[parameters('functionAppName')]",
"location": "[resourceGroup().location]",
"kind": "functionapp",
"dependsOn": [
"[variables('azureFunction_serverFarmResourceId')]",
"[variables('storageAccountResourceId')]",
"[resourceId('Microsoft.Web/certificates', 'testingcert')]"
],
"properties": {
"serverFarmId": "[variables('azureFunction_serverFarmResourceId')]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccounts_name'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),variables('storageAccountApiVersion')).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccounts_name'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),variables('storageAccountApiVersion')).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(parameters('functionAppName'))]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "~10"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('microsoft.insights/components/', parameters('functionApp_applicationInsightsName')), '2015-05-01').InstrumentationKey]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "dotnet"
},
{
"name": "WEBSITE_LOAD_CERTIFICATES",
"value": "required certificate thumprint"
}
]
},
"hostNameSslStates": [
{
"name": "blobcreate-eventgridtrigger-functionapp.azurewebsites.net",//obtained from custom domains flatform features of the function app
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', 'testingcert')).Thumbprint]",
"toUpdate": true
}
]
}
}
]`
add certificates section in template -
{
"type": "Microsoft.Web/certificates",
"name": "[parameters('CertificateName')]",
"apiVersion": "2019-08-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Web/serverFarms/', variables('azurefunction_hostingPlanName'))]"
],
"properties": {
"keyVaultId": "[parameters('keyvaultResourceId')]",
"keyVaultSecretName": "[parameters('invoiceApiCertificateKeyVaultSecretName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('azurefunction_hostingPlanName'))]"
}
}
and then add dependsOn for this certificate in the function app-
[resourceId('Microsoft.Web/certificates', parameters('CertificateName'))]
well, the error is quite obvious, you are trying to add a certificate for blobcreate-eventgridtrigger-functionapp.azurewebsites.net but the dns name on the certificate doesnt match that, hence the error. that is probably not the right way to add a certificate unless its going to be used for SSL termination

How to update a Secret in Azure Key Vault only if changed in ARM templates or check if it exists

I have a production keyvault that keeps a reference of secrets that projects can use, but only if deployed using ARM templates such secrets are not handled by people copy pasting them.
When a new project starts, as part of its deployment script, it will create its own keyvault.
I want to be able to run the templates/scripts as part of CI/CD. And this will today result in the same secret having a new version at each run, even though the value did not change.
How to make it only update the keyvault value when the master vault is updated.
In my deployment.sh script I use the following technique.
SendGridUriWithVersion=$((az group deployment create ... assume that the secret exists ... || az group deployment create ... assume that the secret exists ... ) | jq -r '.properties.outputs.secretUriWithVersion.value')
and it works because in the template there is a parameter, if set, that will retrieve the secret and compare it with the new value and only insert if difference. The original problem is that the deployment fails if the secret is not already set (this happens for the first deployment etc).
But then due to Unix ||, the same script is run again without the parameter set and it will use a condition to not try to get the old value and therefore run successful.
Here are the example in dept:
SecretName="Sendgrid"
SourceSecretName="Sendgrid"
SourceVaultName="io-board"
SourceResourceGroup="io-board"
SendGridUriWithVersion=$((az group deployment create -n ${SecretName}-secret -g $(prefix)-$(projectName)-$(projectEnv) --template-uri https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/${keyVaultName}/secrets/${SecretName}?sourced=true --parameters sourceVault=${SourceVaultName} sourceResourceGroup=${SourceResourceGroup} sourceSecretName=${SourceSecretName} update=true || az group deployment create -n ${SecretName}-secret -g $(prefix)-$(projectName)-$(projectEnv) --template-uri https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/${keyVaultName}/secrets/${SecretName}?sourced=true --parameters sourceVault=${SourceVaultName} sourceResourceGroup=${SourceResourceGroup} sourceSecretName=${SourceSecretName}) | jq -r '.properties.outputs.secretUriWithVersion.value')
The https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/{keyvaultName}/secrets/{secretName}?sourced=true returns a template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string",
"defaultValue": "io-board-data-ingest-dev"
},
"secretName": {
"type": "string",
"metadata": {
"description": "Name of the secret to store in the vault"
},
"defaultValue": "DataStorage"
},
"sourceVaultSubscription": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]"
},
"sourceVault": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]"
},
"sourceResourceGroup": {
"type": "string",
"defaultValue": "[resourceGroup().name]"
},
"sourceSecretName": {
"type": "string"
},
"update": {
"type": "bool",
"defaultValue": false
}
},
"variables": {
"empty": {
"value": ""
},
"test": {
"reference": {
"keyVault": {
"id": "[resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
},
"secretName": "[parameters('secretName')]"
}
}
},
"resources": [
{
"apiVersion": "2018-05-01",
"name": "AddLinkedSecret",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat('https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/',parameters('keyVaultName'),'/secrets/',parameters('secretName'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"existingValue": "[if(parameters('update'),variables('test'),variables('empty'))]",
"secretValue": {
"reference": {
"keyVault": {
"id": "[resourceId(parameters('sourceVaultSubscription'), parameters('sourceResourceGroup'), 'Microsoft.KeyVault/vaults', parameters('sourceVault'))]"
},
"secretName": "[parameters('sourceSecretName')]"
}
}
}
}
}
],
"outputs": {
"secretUriWithVersion": {
"type": "string",
"value": "[reference('AddLinkedSecret').outputs.secretUriWithVersion.value]"
}
}
}
and that template has a nested call to https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/{keyvaultName}/secrets/{secretName} which gives the one with the condition
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string",
"defaultValue": "io-board-data-ingest-dev",
"metadata": {
"description": "Name of the existing vault"
}
},
"secretName": {
"type": "string",
"metadata": {
"description": "Name of the secret to store in the vault"
},
"defaultValue": "DataStorage"
},
"secretValue": {
"type": "securestring",
"metadata": {
"description": "Value of the secret to store in the vault"
}
},
"existingValue": {
"type": "securestring",
"defaultValue": ""
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.KeyVault/vaults/secrets",
"condition": "[not(equals(parameters('existingValue'),parameters('secretValue')))]",
"apiVersion": "2015-06-01",
"name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]",
"properties": {
"value": "[parameters('secretValue')]"
}
}
],
"outputs": {
"secretUriWithVersion": {
"type": "string",
"value": "[reference(resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretName')), '2015-06-01').secretUriWithVersion]"
}
}
}

DWG to DGN conversion using Design Automation API

Is there any possibility for conversion from dwg to dgn using forge design automation api? If yes then what will be the best way to do that? Any suggestion would be helpful.
Yes, you can use the -DGNEXPORT command in your script to accomplish this.
I was trying to get a concrete codes for this question as it requires some tricks and support of seed file on Design Automation. By engineer team's help, now it is working.
Assume we test with Postman. If you are working with v2 of Design Automation, the scripts below demo the usage.
Activity:
{
"HostApplication": "",
"RequiredEngineVersion": "23.1",
"Parameters": {
"InputParameters": [{
"Name": "HostDwg",
"LocalFileName": "$(HostDwg)"
}],
"OutputParameters": [{
"Name": "Result",
"LocalFileName": "result.dgn"
}]
},
"Instruction": {
"CommandLineParameters": null,
"Script":"(command \"_-DGNEXPORT\" \"_V8\" (strcat (getvar \"DWGPREFIX\")
\"result.dgn\") \"_Master\" \"Standard\" (strcat (getvar \"LOCALROOTPREFIX\")
\"Template\\\\V8-Imperial-Seed3D.dgn\"))\n"
},
"Version": 1,
"Id": "CreateActByLISP"
}
WorkItem:
{
"#odata.type": "#ACES.Models.WorkItem",
"Arguments": {
"InputArguments": [
{
"Resource": "http://forge-test.oss-cn-shanghai.aliyuncs.com/test.dwg",
"Name": "HostDwg",
"StorageProvider": "Generic"
}
],
"OutputArguments": [
{
"Name": "Result",
"StorageProvider": "Generic",
"HttpVerb": "POST",
"Resource": null
}
]
},
"ActivityId": "CreateActByLISP",
"Id": ""
}
If working with v3 of Design Automation, the script would be as below. Note: the engine needs 23.1 (Autodesk.AutoCAD+23_1)
Activity
{
"commandLine": [
"$(engine.path)\\accoreconsole.exe /i $(args[HostDwg].path) /s $(settings[script].path)"
],
"parameters": {
"HostDwg": {
"verb": "get",
"description": "Host drawing to be loaded into acad.",
"localName": "$(HostDwg)"
},
"Result": {
"verb": "post",
"description": "Results",
"localName": "result.dgn"
}
},
"engine": "Autodesk.AutoCAD+23_1",
"appbundles": [],
"settings": {
"script": {
"value": "(command \"_-DGNEXPORT\" \"_V8\" (strcat (getvar \"DWGPREFIX\") \"result.dgn\") \"_Master\" \"Standard\" (strcat (getvar \"LOCALROOTPREFIX\") \"Template\\\\V8-Imperial-Seed3D.dgn\"))\n"
}
},
"description": "PlotToPdf for all layouts.",
"id": "myexportdgn"
}
WorkItem:
{
"activityId": "{{your nick name}}.myexportdgn+{{activity alias}}",
"arguments": {
"HostDwg": {
"url": "http://forge-test.oss-cn-shanghai.aliyuncs.com/test.dwg"
},
"Result": {
"verb": "put",
"url": "<your upload url>"
}
}
}

Powerapps difficulty accessing JSON in collection

I'm having difficulty accessing data in a collection, via PowerApps.
I create the collection with this:
Collect(coll15,mt.GetAnswers("3b....da","application/json",{question:"eco"}))
Using Developer Tools -> Network tab -> Response body - the following JSON data is returned:
{
"answers": [
{
"answer": "This is the answer",
"questions": [
"Private vehicle eco renewal"
],
"score": 82.901087775826454
}
]
}
The collection is created.
I then add a gallery control to my page - however the only options I have to bind to the labels are: ThisItem.Value
If I try to enter ThisItem.Value.answer I get the error: Invalid use of '.'
If I enter ThisItem.answers.answer I get the error: Invalid name
This is the swagger file:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "mt",
"description": "mt"
},
"host": "westus.api.cognitive.microsoft.com:443",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [],
"produces": [
"application/json"
],
"paths": {
"/qnamaker/v2.0/knowledgebases/eeeee.....eeeee/generateAnswer": {
"post": {
"summary": "GetAnswers",
"description": "Get answers from qna",
"operationId": "GetAnswers",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "question",
"x-ms-summary": "question",
"title": "question",
"x-ms-visibility": ""
}
},
"default": {
"question": "hi"
},
"required": [
"question"
]
},
"required": true
}
],
"responses": {
"default": {
"description": "default",
"schema": {
"type": "string"
}
}
}
}
}
},
"definitions": {},
"parameters": {},
"responses": {},
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"in": "header",
"name": "Ocp-Apim-Subscription-Key"
}
},
"security": [
{
"oauth2_auth": [
"Offline-Access"
]
}
],
"tags": []
}
Is there any way for me to access the answer text within the collection?
Thanks for any help,
Mark
The problem is that the response type for the operation in the connector definition is string:
"responses": {
"default": {
"description": "default",
"schema": {
"type": "string"
}
}
}
But your response is an object instead. If you update your custom connector to use a typed object instead, you should be able to access the response from the operation. Something along the lines of the schema below:
"responses": {
"default": {
"description": "default",
"schema": {
"type": "object",
"properties": {
"answers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"answer": {
"type": "string"
},
"questions": {
"type": "array",
"items": {
"type": "string"
}
},
"score": {
"type": "number",
"format": "float"
}
}
}
}
}
}
}
},
Notice that in the portal (web.powerapps.com), if you go to your custom connector definition, and select "Edit", you can go to the operation, and select the response you want to edit:
And then use the "Import from sample" option
With that, if you enter an example of a response from the API, it will create the schema for you (which is similar to the one I have above).

Resources