1
1
import * as vscode from "vscode" ;
2
2
import { AtelierAPI } from "../api" ;
3
- import { panel , resolveConnectionSpec , getResolvedConnectionSpec , smExtensionId } from "../extension" ;
4
- import { notIsfs } from "../utils" ;
3
+ import {
4
+ panel ,
5
+ resolveConnectionSpec ,
6
+ getResolvedConnectionSpec ,
7
+ serverManagerApi ,
8
+ resolveUsernameAndPassword ,
9
+ } from "../extension" ;
10
+ import { handleError , isUnauthenticated , notIsfs } from "../utils" ;
5
11
6
12
interface ConnSettings {
7
13
server : string ;
@@ -14,7 +20,6 @@ export async function connectFolderToServerNamespace(): Promise<void> {
14
20
vscode . window . showErrorMessage ( "No folders in the workspace." , "Dismiss" ) ;
15
21
return ;
16
22
}
17
- const serverManagerApi = await getServerManagerApi ( ) ;
18
23
if ( ! serverManagerApi ) {
19
24
vscode . window . showErrorMessage (
20
25
"Connecting a folder to a server namespace requires the [InterSystems Server Manager extension](https://marketplace.visualstudio.com/items?itemName=intersystems-community.servermanager) to be installed and enabled." ,
@@ -31,42 +36,72 @@ export async function connectFolderToServerNamespace(): Promise<void> {
31
36
return {
32
37
label : folder . name ,
33
38
description : folder . uri . fsPath ,
34
- detail : ! conn . server ? undefined : `Currently connected to ${ conn . ns } on ${ conn . server } ` ,
39
+ detail :
40
+ ! conn . server || ! conn . active
41
+ ? "No active server connection"
42
+ : `Currently connected to ${ conn . ns } on ${ conn . server } ` ,
35
43
} ;
36
44
} ) ;
37
45
if ( ! items . length ) {
38
46
vscode . window . showErrorMessage ( "No local folders in the workspace." , "Dismiss" ) ;
39
47
return ;
40
48
}
41
49
const pick =
42
- items . length === 1 && ! items [ 0 ] . detail
50
+ items . length == 1 && ! items [ 0 ] . detail . startsWith ( "Currently" )
43
51
? items [ 0 ]
44
52
: await vscode . window . showQuickPick ( items , { title : "Pick a folder" } ) ;
53
+ if ( ! pick ) return ;
45
54
const folder = vscode . workspace . workspaceFolders . find ( ( el ) => el . name === pick . label ) ;
46
55
// Get user's choice of server
47
56
const options : vscode . QuickPickOptions = { } ;
48
- const serverName : string = await serverManagerApi . pickServer ( undefined , options ) ;
57
+ const serverName : string = await serverManagerApi . pickServer ( folder , options ) ;
49
58
if ( ! serverName ) {
50
59
return ;
51
60
}
52
- // Get its namespace list
53
- const uri = vscode . Uri . parse ( `isfs://${ serverName } /?ns=%SYS` ) ;
54
- await resolveConnectionSpec ( serverName ) ;
61
+ await resolveConnectionSpec ( serverName , undefined , folder ) ;
55
62
// Prepare a displayable form of its connection spec as a hint to the user
56
63
// This will never return the default value (second parameter) because we only just resolved the connection spec.
57
64
const connSpec = getResolvedConnectionSpec ( serverName , undefined ) ;
58
65
const connDisplayString = `${ connSpec . webServer . scheme } ://${ connSpec . webServer . host } :${ connSpec . webServer . port } /${ connSpec . webServer . pathPrefix } ` ;
59
66
// Connect and fetch namespaces
60
- const api = new AtelierAPI ( uri ) ;
61
- const allNamespaces : string [ ] | undefined = await api
67
+ const api = new AtelierAPI ( vscode . Uri . parse ( `isfs://${ serverName } /?ns=%SYS` ) ) ;
68
+ const serverConf = vscode . workspace
69
+ . getConfiguration ( "intersystems" , folder )
70
+ . inspect < { [ key : string ] : any } > ( "servers" ) ;
71
+ if (
72
+ serverConf . workspaceFolderValue &&
73
+ typeof serverConf . workspaceFolderValue [ serverName ] == "object" &&
74
+ ! ( serverConf . workspaceValue && typeof serverConf . workspaceValue [ serverName ] == "object" )
75
+ ) {
76
+ // Need to manually set connection info if the server is defined at the workspace folder level
77
+ api . setConnSpec ( serverName , connSpec ) ;
78
+ }
79
+ const allNamespaces : string [ ] = await api
62
80
. serverInfo ( false )
63
81
. then ( ( data ) => data . result . content . namespaces )
64
- . catch ( ( reason ) => {
65
- // Notify user about serverInfo failure
66
- vscode . window . showErrorMessage (
67
- reason . message || `Failed to fetch namespace list from server at ${ connDisplayString } ` ,
68
- "Dismiss"
69
- ) ;
82
+ . catch ( async ( error ) => {
83
+ if ( error ?. statusCode == 401 && isUnauthenticated ( api . config . username ) ) {
84
+ // Attempt to resolve username and password and try again
85
+ const newSpec = await resolveUsernameAndPassword ( api . config . serverName , connSpec ) ;
86
+ if ( newSpec ) {
87
+ // We were able to resolve credentials, so try again
88
+ api . setConnSpec ( api . config . serverName , newSpec ) ;
89
+ return api
90
+ . serverInfo ( false )
91
+ . then ( ( data ) => data . result . content . namespaces )
92
+ . catch ( async ( err ) => {
93
+ handleError ( err , `Failed to fetch namespace list from server at ${ connDisplayString } .` ) ;
94
+ return undefined ;
95
+ } ) ;
96
+ } else {
97
+ handleError (
98
+ `Unauthenticated access rejected by '${ api . serverId } '.` ,
99
+ `Failed to fetch namespace list from server at ${ connDisplayString } .`
100
+ ) ;
101
+ return undefined ;
102
+ }
103
+ }
104
+ handleError ( error , `Failed to fetch namespace list from server at ${ connDisplayString } .` ) ;
70
105
return undefined ;
71
106
} ) ;
72
107
// Clear the panel entry created by the connection
@@ -90,22 +125,34 @@ export async function connectFolderToServerNamespace(): Promise<void> {
90
125
}
91
126
// Update folder's config object
92
127
const config = vscode . workspace . getConfiguration ( "objectscript" , folder ) ;
93
- const conn : any = config . inspect ( "conn" ) . workspaceFolderValue ;
94
- await config . update ( "conn" , { ...conn , server : serverName , ns : namespace , active : true } ) ;
95
- }
96
-
97
- async function getServerManagerApi ( ) : Promise < any > {
98
- const targetExtension = vscode . extensions . getExtension ( smExtensionId ) ;
99
- if ( ! targetExtension ) {
100
- return undefined ;
101
- }
102
- if ( ! targetExtension . isActive ) {
103
- await targetExtension . activate ( ) ;
104
- }
105
- const api = targetExtension . exports ;
106
-
107
- if ( ! api ) {
108
- return undefined ;
128
+ if ( vscode . workspace . workspaceFile && items . length == 1 ) {
129
+ // Ask the user if they want to enable the connection at the workspace or folder level.
130
+ // Only allow this when there is a single client-side folder in the workspace because
131
+ // the server may be configured at the workspace folder level.
132
+ const answer = await vscode . window . showQuickPick (
133
+ [
134
+ { label : `Workspace Folder ${ folder . name } ` , detail : folder . uri . toString ( true ) } ,
135
+ { label : "Workspace File" , detail : vscode . workspace . workspaceFile . toString ( true ) } ,
136
+ ] ,
137
+ { title : "Store the server connection at the workspace or folder level?" }
138
+ ) ;
139
+ if ( ! answer ) return ;
140
+ if ( answer . label == "Workspace File" ) {
141
+ // Enable the connection at the workspace level
142
+ const conn : any = config . inspect ( "conn" ) . workspaceValue ;
143
+ await config . update (
144
+ "conn" ,
145
+ { ...conn , server : serverName , ns : namespace , active : true } ,
146
+ vscode . ConfigurationTarget . Workspace
147
+ ) ;
148
+ return ;
149
+ }
109
150
}
110
- return api ;
151
+ // Enable the connection at the workspace folder level
152
+ const conn : any = config . inspect ( "conn" ) . workspaceFolderValue ;
153
+ await config . update (
154
+ "conn" ,
155
+ { ...conn , server : serverName , ns : namespace , active : true } ,
156
+ vscode . ConfigurationTarget . WorkspaceFolder
157
+ ) ;
111
158
}
0 commit comments