1+ import { isAxiosError } from "axios" ;
12import {
23 type Workspace ,
34 type WorkspaceAgent ,
@@ -23,40 +24,47 @@ import { type Logger } from "../logging/logger";
2324import type { SessionData , SessionState } from "../deployment/sessionStore" ;
2425
2526export enum WorkspaceQuery {
26- Mine = "owner:me " ,
27- All = "" ,
28- Shared = "shared:true " ,
27+ Mine = "mine " ,
28+ All = "all " ,
29+ Shared = "shared" ,
2930}
3031
31- /** Per-view rendering behavior, keyed by workspace query. */
32+ type SignedInSession = Extract < SessionData , { kind : "signedIn" } > ;
33+
34+ /** Per-view rendering behavior and search query, keyed by workspace view. */
3235interface WorkspaceQueryConfig {
3336 readonly showOwner : boolean ;
3437 readonly showMetadata : boolean ;
35- readonly excludeOwn : boolean ;
38+ readonly getQuery : ( session : SignedInSession ) => string ;
3639}
3740
3841const WORKSPACE_QUERY_CONFIG = {
3942 [ WorkspaceQuery . Mine ] : {
4043 showOwner : false ,
4144 showMetadata : true ,
42- excludeOwn : false ,
45+ getQuery : ( ) => "owner:me" ,
4346 } ,
4447 [ WorkspaceQuery . All ] : {
4548 showOwner : true ,
4649 showMetadata : false ,
47- excludeOwn : false ,
50+ getQuery : ( ) => "" ,
4851 } ,
4952 [ WorkspaceQuery . Shared ] : {
5053 showOwner : true ,
5154 showMetadata : false ,
52- // `shared:true` also returns workspaces we own and shared out; exclude
53- // them to leave only those shared with us .
54- excludeOwn : true ,
55+ // Only workspaces shared with the user; excludes workspaces the user
56+ // owns and shared with others. Requires Coder 2.27.0+ .
57+ getQuery : ( session ) => `shared_with_user: ${ session . user . id } ` ,
5558 } ,
5659} as const satisfies Record < WorkspaceQuery , WorkspaceQueryConfig > ;
5760
5861export interface WorkspaceProviderOptions {
5962 readonly refreshIntervalMs ?: number ;
63+ /**
64+ * Called when the server rejects the workspaces query with HTTP 400,
65+ * which indicates a deployment that does not support the query filter.
66+ */
67+ readonly onQueryRejected ?: ( ) => void ;
6068}
6169
6270/**
@@ -127,6 +135,9 @@ export class WorkspaceProvider
127135 this . logger . warn ( "Failed to fetch workspaces:" , error ) ;
128136 hadError = true ;
129137 this . setWorkspaces ( [ ] ) ;
138+ if ( isAxiosError ( error ) && error . response ?. status === 400 ) {
139+ this . options . onQueryRejected ?.( ) ;
140+ }
130141 }
131142 } while ( this . refetchPending && ! this . disposed && this . visible ) ;
132143 } finally {
@@ -163,14 +174,14 @@ export class WorkspaceProvider
163174 }
164175
165176 const resp = await this . client . getWorkspaces ( {
166- q : this . getWorkspacesQuery ,
177+ q : this . config . getQuery ( session ) ,
167178 } ) ;
168179
169180 if ( this . sessionChangedSince ( session ) ) {
170181 return null ;
171182 }
172183
173- const workspaces = this . filterWorkspaces ( resp . workspaces , session ) ;
184+ const workspaces = resp . workspaces ;
174185 const oldWatcherIds = [ ...this . agentWatchers . keys ( ) ] ;
175186 const reusedWatcherIds : string [ ] = [ ] ;
176187
@@ -221,18 +232,6 @@ export class WorkspaceProvider
221232 return this . sessionState . current !== session ;
222233 }
223234
224- private filterWorkspaces (
225- workspaces : readonly Workspace [ ] ,
226- session : Extract < SessionData , { kind : "signedIn" } > ,
227- ) : readonly Workspace [ ] {
228- if ( ! this . config . excludeOwn ) {
229- return workspaces ;
230- }
231- return workspaces . filter (
232- ( workspace ) => workspace . owner_id !== session . user . id ,
233- ) ;
234- }
235-
236235 /**
237236 * Either start or stop the refresh timer based on visibility.
238237 *
0 commit comments