@@ -215,18 +215,52 @@ const resolvedConnSpecs = new Map<string, any>();
215215/**
216216 * If servermanager extension is available, fetch the connection spec unless already cached.
217217 * 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.
219220 */
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+ } ;
227256 }
228257 }
229258 }
259+
260+ if ( connSpec ) {
261+ await resolvePassword ( connSpec ) ;
262+ resolvedConnSpecs . set ( serverName , connSpec ) ;
263+ }
230264}
231265
232266async function resolvePassword ( serverSpec , ignoreUnauthenticated = false ) : Promise < void > {
@@ -261,7 +295,22 @@ async function resolvePassword(serverSpec, ignoreUnauthenticated = false): Promi
261295
262296/** Accessor for the cache of resolved connection specs */
263297export 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 ;
265314}
266315
267316export async function checkConnection (
@@ -732,15 +781,20 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
732781 vscode . workspace . workspaceFolders ?. map ( ( workspaceFolder ) => {
733782 const uri = workspaceFolder . uri ;
734783 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 ;
736788 toCheck . set ( serverName , uri ) ;
737789 } ) ;
738790 for await ( const oneToCheck of toCheck ) {
739791 const serverName = oneToCheck [ 0 ] ;
740792 const uri = oneToCheck [ 1 ] ;
741793 try {
742794 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 ) ;
744798 } finally {
745799 await checkConnection ( true , uri , true ) ;
746800 }
@@ -1518,46 +1572,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
15181572
15191573 // The API we export
15201574 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,
15611577 serverDocumentUriForUri ( uri : vscode . Uri ) : vscode . Uri {
15621578 const { apiTarget } = connectionTarget ( uri ) ;
15631579 if ( typeof apiTarget === "string" ) {
@@ -1589,6 +1605,66 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
15891605 return extensionApi ;
15901606}
15911607
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+
15921668export function deactivate ( ) : void {
15931669 if ( workspaceState ) {
15941670 workspaceState . update ( "openedClasses" , openedClasses ) ;
0 commit comments