Skip to content

Commit

Permalink
Merge pull request #1394 from microsoft/grarchib-aa4am-1357
Browse files Browse the repository at this point in the history
Update to check all connection references
  • Loading branch information
mikefactorial authored Oct 29, 2021
2 parents db5076a + 7a8b861 commit b322377
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 127 deletions.
1 change: 1 addition & 0 deletions coe-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"main": "index.js",
"scripts": {
"test": "jest",
"test-debug": "node --inspect ./node_modules/jest/bin/jest.js -i",
"build": "tsc",
"lint": "eslint . & echo 'lint complete'",
"prod": "tsc && node ./built/src/index.js",
Expand Down
263 changes: 160 additions & 103 deletions coe-cli/src/commands/powerplatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class PowerPlatformCommand {
createAADCommand: () => AADCommand
createAA4AMCommand: () => AA4AMCommand
logger: winston.Logger
readline: readline.ReadLine
readline: any
outputText: (text: string) => void

constructor(logger: winston.Logger, defaultReadline: readline.ReadLine = null) {
Expand Down Expand Up @@ -249,7 +249,7 @@ class PowerPlatformCommand {

let clientid = aad.getAADApplication(addInstallArgs)

if (connectorMatch[0].connectionparameters?.length == 0 || connectorMatch[0].connectionparameters == "{}" ) {
if (typeof connectorMatch[0].connectionparameters === "undefined" || connectorMatch[0].connectionparameters?.length == 0 || connectorMatch[0].connectionparameters == "{}" ) {
this.logger?.info("Applying default connection information")
connectorMatch[0].connectionparameters = JSON.stringify({
"token": {
Expand Down Expand Up @@ -298,7 +298,7 @@ class PowerPlatformCommand {

let connectionParameters = JSON.parse(connectorMatch[0].connectionparameters)

if (connectionParameters.token.oAuthSettings.clientId != clientid || connectionParameters.token.oAuthSettings.properties.AzureActiveDirectoryResourceId != "499b84ac-1321-427f-aa17-267ca6975798") {
if (typeof connectionParameters.token != undefined && connectionParameters.token.oAuthSettings.clientId != clientid || connectionParameters.token.oAuthSettings.properties.AzureActiveDirectoryResourceId != "499b84ac-1321-427f-aa17-267ca6975798") {
this.logger?.debug("Connector needs update")
let powerAppsUrl = this.mapEndpoint("powerapps", args.endpoint)
let bapUrl = this.mapEndpoint("bap", args.endpoint)
Expand Down Expand Up @@ -329,47 +329,50 @@ class PowerPlatformCommand {
url = `${powerAppsUrl}providers/Microsoft.PowerApps/apis/${connectorName}/?$filter=environment eq '${environment}'&api-version=2016-11-01`
let updateConnection: AxiosResponse<any>
try {
// Based on work of paconn update of
// https://github.com/microsoft/PowerPlatformConnectors/blob/1b81ada7b083302b59c33d9ed6b14cb2ac8a0785/tools/paconn-cli/paconn/operations/upsert.py
if ( typeof connectionParameters.token != "undefined" )
{
// Based on work of paconn update of
// https://github.com/microsoft/PowerPlatformConnectors/blob/1b81ada7b083302b59c33d9ed6b14cb2ac8a0785/tools/paconn-cli/paconn/operations/upsert.py

if (typeof data.properties.connectionParameters.token.oAuthSettings.customParameters !== "undefined") {
data.properties.connectionParameters.token.oAuthSettings.customParameters.resourceUri.value = "499b84ac-1321-427f-aa17-267ca6975798"
}

if (typeof data.properties.connectionParameters.token.oAuthSettings.properties !== "undefined") {
data.properties.connectionParameters.token.oAuthSettings.properties.AzureActiveDirectoryResourceId = "499b84ac-1321-427f-aa17-267ca6975798"
}

let update = {
properties: {
connectionParameters: {
token: {
oAuthSettings: {
clientId: clientid,
clientSecret: secret.clientSecret,
properties: data.properties.connectionParameters.token.oAuthSettings.properties,
customParameters: data.properties.connectionParameters.token.oAuthSettings.customParameters,
identityProvider: data.properties.connectionParameters.token.oAuthSettings.identityProvider,
redirectMode: data.properties.connectionParameters.token.oAuthSettings.redirectMode,
scopes: data.properties.connectionParameters.token.oAuthSettings.scopes
},
type: "oAuthSetting"
}
},
backendService: data.properties.backendService,
environment: { name: environment },
description: data.properties.description,
openApiDefinition: original.data,
policyTemplateInstances: data.properties.policyTemplateInstances
if (typeof connectionParameters.token.oAuthSettings.customParameters !== "undefined") {
connectionParameters.token.oAuthSettings.customParameters.resourceUri.value = "499b84ac-1321-427f-aa17-267ca6975798"
}

if (typeof connectionParameters.token.oAuthSettings.properties !== "undefined") {
connectionParameters.token.oAuthSettings.properties.AzureActiveDirectoryResourceId = "499b84ac-1321-427f-aa17-267ca6975798"
}

}
updateConnection = await this.getAxios().patch(url, update, {
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json;charset=UTF-8"
let update = {
properties: {
connectionParameters: {
token: {
oAuthSettings: {
clientId: clientid,
clientSecret: secret.clientSecret,
properties: connectionParameters.token.oAuthSettings.properties,
customParameters: connectionParameters.token.oAuthSettings.customParameters,
identityProvider: connectionParameters.token.oAuthSettings.identityProvider,
redirectMode: connectionParameters.token.oAuthSettings.redirectMode,
scopes: connectionParameters.token.oAuthSettings.scopes
},
type: "oAuthSetting"
}
},
backendService: data.properties.backendService,
environment: { name: environment },
description: data.properties.description,
openApiDefinition: original.data,
policyTemplateInstances: data.properties.policyTemplateInstances
}

}
})
updateConnection = await this.getAxios().patch(url, update, {
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json;charset=UTF-8"
}
})
}
} catch (err) {
this.logger?.error(err)
}
Expand Down Expand Up @@ -421,6 +424,11 @@ class PowerPlatformCommand {
}

async fixConnectionReferences(environment: string, solutions: any, args: PowerPlatformImportSolutionArguments): Promise<void> {
if ( typeof solutions == undefined || typeof solutions.value == undefined || solutions.value.length <= 0 ) {
this.logger?.error("No solution found")
return
}

this.logger?.info("Check connection reference")

let environmentUrl = Environment.getEnvironmentUrl(args.environment, args.settings)
Expand All @@ -429,96 +437,145 @@ class PowerPlatformCommand {

let aadInfo = (await this.getSecureJson(`${environmentUrl}api/data/v9.0/systemusers?$filter=systemuserid eq '${whoAmI.UserId}'&$select=azureactivedirectoryobjectid`, args.accessToken))

this.logger?.debug('Query environment connecctions')
let solutionComponentTypes = (await this.getSecureJson(`${environmentUrl}api/data/v9.0/solutioncomponentdefinitions?$filter=primaryentityname eq 'connectionreference'`, args.accessToken))

let solutionComponentType = solutionComponentTypes.value[0].solutioncomponenttype

let connectionReferenceSolutionComponents = (await this.getSecureJson(`${environmentUrl}api/data/v9.0/solutioncomponents?$orderby=componenttype&$filter=_solutionid_value eq '${solutions.value[0].solutionid}' and componenttype eq ${solutionComponentType}`, args.accessToken))

this.logger?.debug('Query environment connections')
let powerAppsUrl = this.mapEndpoint("powerapps", args.endpoint)
let bapUrl = this.mapEndpoint("bap", args.endpoint)
let token = args.accessTokens[bapUrl]

// Source: Microsoft.PowerApps.Administration.PowerShell.psm1
let url = `${powerAppsUrl}providers/Microsoft.PowerApps/scopes/admin/environments/${environment}/connections?api-version=2016-11-01`

let connection: any[]

let stopped = false
let connected : string[] = []
let connection: any = null
while ( !stopped ) {
let connectionResults = await this.getAxios().get(url, {
headers: {
Authorization: `Bearer ${token}`
for ( var i = 0; i < connectionReferenceSolutionComponents.value.length; i++)
{
let connectionReferenceId = connectionReferenceSolutionComponents.value[i].objectid

if ( connected.indexOf(connectionReferenceId) >= 0) {
if ( connected.length == connectionReferenceSolutionComponents.value.length ) {
stopped = true
break
}
continue
}
})

this.logger?.info(`Found ${connectionResults.data.value.length} connection(s)`)

connection = connectionResults.data.value.filter((c: any) => c.properties.createdBy?.id == aadInfo.value[0].azureactivedirectoryobjectid && c.properties.apiId?.endsWith('/shared_commondataservice'))
let connectionReferenceUrl = `${environmentUrl}api/data/v9.0/connectionreferences?$filter=connectionreferenceid eq '${connectionReferenceId}'`
let connectionReferences = (await this.getSecureJson(connectionReferenceUrl, args.accessToken)).value

if (connection.length == 0) {
this.logger?.error('No Microsoft Dataverse (Legacy Found) in environment ${environmentUrl}.')
this.readline = ReadLineManagement.setupReadLine(this.readline)
let result = await new Promise((resolve, reject) => {
this.readline.question("Create connection now (Y/n)? ", (answer: string) => {
if ( answer.length == 0 || answer.toLowerCase() == 'y') {
resolve('y')
} else {
resolve('n')
let unconnectedConnectionReferences = connectionReferences.filter((con: any) => con.connectionid == null)
let connectedConnectionReferences = connectionReferences.filter((con: any) => con.connectionid != null)

if ( connectedConnectionReferences.length == 1 ) {
let connectionResults = await this.getAxios().get(url, {
headers: {
Authorization: `Bearer ${token}`
}
})

connection = connectionResults.data.value.filter((c: any) => c.name == connectedConnectionReferences[0].connectionid)

if ( connection.length == 1 ) {
connected.push(connectionReferenceId)
continue
}
}

let connectionTypeParts = unconnectedConnectionReferences[0]?.connectorid?.split('/')
let connectionType = unconnectedConnectionReferences.length > 0 && typeof connectionTypeParts !== "undefined" ? connectionTypeParts[connectionTypeParts.length - 1] : ""

if ( connectionType == '' && connectedConnectionReferences.length > 0 ) {
connectionTypeParts = connectedConnectionReferences[0]?.connectorid?.split('/')
connectionType = connectedConnectionReferences.length > 0 && typeof connectionTypeParts !== "undefined" ? connectionTypeParts[connectionTypeParts.length - 1] : ""
}


let connectionResults = await this.getAxios().get(url, {
headers: {
Authorization: `Bearer ${token}`
}
})
if (result == 'y') {
this.outputText(`Create a connection by open page https://make.powerapps.com/environments/${environment}/connections/available?apiName=shared_commondataservice`)
result = await new Promise((resolve, reject) => {
this.readline.question("Check again now (Y/n)? ", (answer: string) => {

connection = connectionResults.data.value.filter((c: any) => c.properties.createdBy?.id == aadInfo.value[0].azureactivedirectoryobjectid && c.properties.apiId?.endsWith(`/${connectionType}`))

if ( connection.length == 0 )
{
if ( unconnectedConnectionReferences.length > 0 ) {
this.logger?.error(`Missing '${unconnectedConnectionReferences[0].connectionreferencedisplayname}' connection reference`)
}

this.readline = ReadLineManagement.setupReadLine(this.readline)
let result = await new Promise((resolve, reject) => {
this.readline.question("Create connection now (Y/n)? ", (answer: string) => {
if ( answer.length == 0 || answer.toLowerCase() == 'y') {
resolve('y')
} else {
resolve('n')
}
})
})
})
if (result == 'y') {
this.outputText(`Create a connection by opening page https://make.powerapps.com/environments/${environment}/connections/available?apiName=${connectionType} in your browser`)
result = await new Promise((resolve, reject) => {
this.readline.question("Check again now (Y/n)? ", (answer: string) => {
if ( answer.length == 0 || answer.toLowerCase() == 'y') {
resolve('y')
} else {
resolve('n')
}
})
})
}
if (result == 'n') {
this.logger?.info("Exiting install")
stopped = true
return Promise.resolve();
}

connectionResults = await this.getAxios().get(url, {
headers: {
Authorization: `Bearer ${token}`
}
})
}
if (result == 'n') {
this.logger?.info("Exiting install")
stopped = true
return Promise.resolve();

connection = connectionResults.data.value.filter((c: any) => c.properties.createdBy?.id == aadInfo.value[0].azureactivedirectoryobjectid && c.properties.apiId?.endsWith(`/${connectionType}`))

if ( connection.length > 0 )
{
try {
let update = {
"connectionid": `${connection[0].name}`
}

await this.getAxios().patch(`${environmentUrl}api/data/v9.0/connectionreferences(${unconnectedConnectionReferences[0].connectionreferenceid})`, update, {
headers: {
'Authorization': 'Bearer ' + args.accessToken,
'Content-Type': 'application/json',
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
'If-Match': '*'
}
})
this.logger?.info("Connection reference updated")

connected.push(connectionReferenceId)
} catch (err) {
this.logger?.error(err)
}
}
} else {
// Found a connection
stopped = true
}
}

this.readline?.close()
this.readline = null

let connectionReferences = (await this.getSecureJson(`${environmentUrl}api/data/v9.0/connectionreferences?$filter=solutionid eq '${solutions.value[0].solutionid}'`, args.accessToken)).value
let connectionMatch = connectionReferences?.filter((c: any) => c.connectionreferencelogicalname.startsWith('cat_CDSDevOps'))

if (typeof connectionMatch === "undefined" || connectionMatch?.length == 0) {
this.logger?.info('Dataverse Connection not found')
return Promise.resolve();
} else {
this.logger?.info("Connection found")
if (connectionMatch[0].connectionid == null) {
this.logger?.info("Connection id needs to be updated")
let update = {
"connectionid": `${connection[0].name}`
}
try {
await this.getAxios().patch(`${environmentUrl}api/data/v9.0/connectionreferences(${connectionMatch[0].connectionreferenceid})`, update, {
headers: {
'Authorization': 'Bearer ' + args.accessToken,
'Content-Type': 'application/json',
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
'If-Match': '*'
}
})
this.logger?.info("Connection reference updated")
} catch (err) {
this.logger?.error(err)
}
} else {
this.logger?.debug("Connection already connected")
}
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion coe-cli/test/commands/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Add', () => {
args.name = "Sample"

// Act
jest.setTimeout(20000);
jest.setTimeout(30000);
await command.add(args)

// Assert
Expand Down
Loading

0 comments on commit b322377

Please sign in to comment.