@@ -215,18 +215,52 @@ const resolvedConnSpecs = new Map<string, any>();
215
215
/**
216
216
* If servermanager extension is available, fetch the connection spec unless already cached.
217
217
* Prompt for credentials if necessary.
218
- * @param serverName authority element of an isfs uri, or `objectscript.conn.server` property
218
+ * @param serverName authority element of an isfs uri, or `objectscript.conn.server` property, or the name of a root folder with an `objectscript.conn.docker-compose` property object
219
+ * @param uri if passed, re-check the `objectscript.conn.docker-compose` case in case servermanager API couldn't do that because we're still running our own `activate` method.
219
220
*/
220
- export async function resolveConnectionSpec ( serverName : string ) : Promise < void > {
221
- if ( serverManagerApi && serverManagerApi . getServerSpec ) {
222
- if ( serverName && serverName !== "" && ! resolvedConnSpecs . has ( serverName ) ) {
223
- const connSpec = await serverManagerApi . getServerSpec ( serverName ) ;
224
- if ( connSpec ) {
225
- await resolvePassword ( connSpec ) ;
226
- resolvedConnSpecs . set ( serverName , connSpec ) ;
221
+ export async function resolveConnectionSpec ( serverName : string , uri ?: vscode . Uri ) : Promise < void > {
222
+ if ( ! serverManagerApi || ! serverManagerApi . getServerSpec || serverName === "" ) {
223
+ return ;
224
+ }
225
+ if ( resolvedConnSpecs . has ( serverName ) ) {
226
+ // Already resolved
227
+ return ;
228
+ }
229
+ if ( ! vscode . workspace . getConfiguration ( "intersystems.servers" , null ) . has ( serverName ) ) {
230
+ // When not a defined server see it already resolved as a foldername that matches case-insensitively
231
+ if ( getResolvedConnectionSpec ( serverName , undefined ) ) {
232
+ return ;
233
+ }
234
+ }
235
+
236
+ let connSpec = await serverManagerApi . getServerSpec ( serverName ) ;
237
+
238
+ if ( ! connSpec && uri ) {
239
+ // Caller passed uri as a signal to process any docker-compose settings
240
+ const { configName } = connectionTarget ( uri ) ;
241
+ if ( config ( "conn" , configName ) [ "docker-compose" ] ) {
242
+ const serverForUri = await asyncServerForUri ( uri ) ;
243
+ if ( serverForUri ) {
244
+ connSpec = {
245
+ name : serverForUri . serverName ,
246
+ webServer : {
247
+ scheme : serverForUri . scheme ,
248
+ host : serverForUri . host ,
249
+ port : serverForUri . port ,
250
+ pathPrefix : serverForUri . pathPrefix ,
251
+ } ,
252
+ username : serverForUri . username ,
253
+ password : serverForUri . password ? serverForUri . password : undefined ,
254
+ description : `Server for workspace folder '${ serverName } '` ,
255
+ } ;
227
256
}
228
257
}
229
258
}
259
+
260
+ if ( connSpec ) {
261
+ await resolvePassword ( connSpec ) ;
262
+ resolvedConnSpecs . set ( serverName , connSpec ) ;
263
+ }
230
264
}
231
265
232
266
async function resolvePassword ( serverSpec , ignoreUnauthenticated = false ) : Promise < void > {
@@ -261,7 +295,22 @@ async function resolvePassword(serverSpec, ignoreUnauthenticated = false): Promi
261
295
262
296
/** Accessor for the cache of resolved connection specs */
263
297
export function getResolvedConnectionSpec ( key : string , dflt : any ) : any {
264
- return resolvedConnSpecs . has ( key ) ? resolvedConnSpecs . get ( key ) : dflt ;
298
+ let spec = resolvedConnSpecs . get ( key ) ;
299
+ if ( spec ) {
300
+ return spec ;
301
+ }
302
+
303
+ // Try a case-insensitive match
304
+ key = resolvedConnSpecs . keys ( ) . find ( ( oneKey ) => oneKey . toLowerCase ( ) === key . toLowerCase ( ) ) ;
305
+ if ( key ) {
306
+ spec = resolvedConnSpecs . get ( key ) ;
307
+ if ( spec ) {
308
+ return spec ;
309
+ }
310
+ }
311
+
312
+ // Return the default if not found
313
+ return dflt ;
265
314
}
266
315
267
316
export async function checkConnection (
@@ -732,15 +781,20 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
732
781
vscode . workspace . workspaceFolders ?. map ( ( workspaceFolder ) => {
733
782
const uri = workspaceFolder . uri ;
734
783
const { configName } = connectionTarget ( uri ) ;
735
- const serverName = notIsfs ( uri ) ? config ( "conn" , configName ) . server : configName ;
784
+ const conn = config ( "conn" , configName ) ;
785
+
786
+ // When docker-compose object is defined don't fall back to server name, which may have come from user-level settings
787
+ const serverName = notIsfs ( uri ) && ! conn [ "docker-compose" ] ? conn . server : configName ;
736
788
toCheck . set ( serverName , uri ) ;
737
789
} ) ;
738
790
for await ( const oneToCheck of toCheck ) {
739
791
const serverName = oneToCheck [ 0 ] ;
740
792
const uri = oneToCheck [ 1 ] ;
741
793
try {
742
794
try {
743
- await resolveConnectionSpec ( serverName ) ;
795
+ // Pass the uri to resolveConnectionSpec so it will fall back to docker-compose logic if required.
796
+ // Necessary because we are in our activate method, so its call to the Server Manager API cannot call back to our API to do that.
797
+ await resolveConnectionSpec ( serverName , uri ) ;
744
798
} finally {
745
799
await checkConnection ( true , uri , true ) ;
746
800
}
@@ -1518,46 +1572,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
1518
1572
1519
1573
// The API we export
1520
1574
const extensionApi = {
1521
- serverForUri ( uri : vscode . Uri ) : any {
1522
- const { apiTarget } = connectionTarget ( uri ) ;
1523
- const api = new AtelierAPI ( apiTarget ) ;
1524
-
1525
- // This function intentionally no longer exposes the password for a named server UNLESS it is already exposed as plaintext in settings.
1526
- // API client extensions should use Server Manager 3's authentication provider to request a missing password themselves,
1527
- // which will require explicit user consent to divulge the password to the requesting extension.
1528
-
1529
- const {
1530
- serverName,
1531
- active,
1532
- host = "" ,
1533
- https,
1534
- port,
1535
- pathPrefix,
1536
- username,
1537
- password,
1538
- ns = "" ,
1539
- apiVersion,
1540
- serverVersion,
1541
- } = api . config ;
1542
- return {
1543
- serverName,
1544
- active,
1545
- scheme : https ? "https" : "http" ,
1546
- host,
1547
- port,
1548
- pathPrefix,
1549
- username,
1550
- password :
1551
- serverName === ""
1552
- ? password
1553
- : vscode . workspace
1554
- . getConfiguration ( `intersystems.servers.${ serverName . toLowerCase ( ) } ` , uri )
1555
- . get ( "password" ) ,
1556
- namespace : ns ,
1557
- apiVersion : active ? apiVersion : undefined ,
1558
- serverVersion : active ? serverVersion : undefined ,
1559
- } ;
1560
- } ,
1575
+ serverForUri,
1576
+ asyncServerForUri,
1561
1577
serverDocumentUriForUri ( uri : vscode . Uri ) : vscode . Uri {
1562
1578
const { apiTarget } = connectionTarget ( uri ) ;
1563
1579
if ( typeof apiTarget === "string" ) {
@@ -1589,6 +1605,66 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
1589
1605
return extensionApi ;
1590
1606
}
1591
1607
1608
+ // This function is exported as one of our API functions but is also used internally
1609
+ // for example to implement the async variant capable of resolving docker port number.
1610
+ function serverForUri ( uri : vscode . Uri ) : any {
1611
+ const { apiTarget } = connectionTarget ( uri ) ;
1612
+ const api = new AtelierAPI ( apiTarget ) ;
1613
+
1614
+ // This function intentionally no longer exposes the password for a named server UNLESS it is already exposed as plaintext in settings.
1615
+ // API client extensions should use Server Manager 3's authentication provider to request a missing password themselves,
1616
+ // which will require explicit user consent to divulge the password to the requesting extension.
1617
+ const {
1618
+ serverName,
1619
+ active,
1620
+ host = "" ,
1621
+ https,
1622
+ port,
1623
+ pathPrefix,
1624
+ username,
1625
+ password,
1626
+ ns = "" ,
1627
+ apiVersion,
1628
+ serverVersion,
1629
+ } = api . config ;
1630
+ return {
1631
+ serverName,
1632
+ active,
1633
+ scheme : https ? "https" : "http" ,
1634
+ host,
1635
+ port,
1636
+ pathPrefix,
1637
+ username,
1638
+ password :
1639
+ serverName === ""
1640
+ ? password
1641
+ : vscode . workspace . getConfiguration ( `intersystems.servers.${ serverName . toLowerCase ( ) } ` , uri ) . get ( "password" ) ,
1642
+ namespace : ns ,
1643
+ apiVersion : active ? apiVersion : undefined ,
1644
+ serverVersion : active ? serverVersion : undefined ,
1645
+ } ;
1646
+ }
1647
+
1648
+ // An async variant capable of resolving docker port number.
1649
+ // It is exported as one of our API functions but is also used internally.
1650
+ async function asyncServerForUri ( uri : vscode . Uri ) : Promise < any > {
1651
+ const server = serverForUri ( uri ) ;
1652
+ if ( ! server . port ) {
1653
+ let { apiTarget } = connectionTarget ( uri ) ;
1654
+ if ( apiTarget instanceof vscode . Uri ) {
1655
+ apiTarget = vscode . workspace . getWorkspaceFolder ( apiTarget ) ?. name ;
1656
+ }
1657
+ const { port : dockerPort , docker : withDocker } = await portFromDockerCompose ( apiTarget ) ;
1658
+ if ( withDocker && dockerPort ) {
1659
+ server . port = dockerPort ;
1660
+ server . host = "localhost" ;
1661
+ server . pathPrefix = "" ;
1662
+ server . https = false ;
1663
+ }
1664
+ }
1665
+ return server ;
1666
+ }
1667
+
1592
1668
export function deactivate ( ) : void {
1593
1669
if ( workspaceState ) {
1594
1670
workspaceState . update ( "openedClasses" , openedClasses ) ;
0 commit comments