-
Notifications
You must be signed in to change notification settings - Fork 332
Implement functionality to open and close projects in dashboard #13994
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7bf196f
610ca97
b843942
84907be
f8f8db9
d8dbc8b
1afec1f
4b203da
786bd8f
235d92f
ea87127
f5a3fed
cb8fbd0
468a1b9
94220cc
7b10100
4f1804f
2c78488
b2ebab5
875dccd
41d2539
8e2bebd
c060712
75913a9
a2ea49d
d1a79ba
f800dfc
c9c872e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,19 +56,24 @@ export class ProjectManager { | |
private reconnecting = false | ||
private resolvers = new Map<number, (value: never) => void>() | ||
private rejecters = new Map<number, (reason?: JSONRPCError) => void>() | ||
private socketPromise: Promise<WebSocket> | ||
private socketPromise: Promise<WebSocket> | null = null | ||
|
||
/** Create a {@link ProjectManager} */ | ||
constructor( | ||
private readonly connectionUrl: string, | ||
public readonly rootDirectory: Path, | ||
) { | ||
this.socketPromise = this.reconnect() | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
if (!enableProjectService.value) { | ||
this.socketPromise = this.reconnect() | ||
} | ||
Comment on lines
+66
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't make use of Vue reactivity here, so you may just add return flagsStore.getState().featureFlags[key] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, one thing looks strange: we reconnect only when ProjectService is not enabled? I thought it should be the other way round... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, there's some confusion in the naming. The new logic is called ProjectService because there is a ProjectService.scala which I ported to TypeScript. In other words, the project service is the new logic in dashboard and the project manager is the old java process |
||
} | ||
|
||
/** Begin reconnecting the {@link WebSocket}. */ | ||
reconnect() { | ||
if (this.reconnecting) { | ||
if (this.reconnecting && this.socketPromise) { | ||
return this.socketPromise | ||
} | ||
this.reconnecting = true | ||
|
@@ -129,8 +134,10 @@ export class ProjectManager { | |
|
||
/** Dispose of the {@link ProjectManager}. */ | ||
async dispose() { | ||
const socket = await this.socketPromise | ||
socket.close() | ||
if (this.socketPromise) { | ||
const socket = await this.socketPromise | ||
socket.close() | ||
} | ||
} | ||
|
||
/** Get the state of a project given its path. */ | ||
|
@@ -157,7 +164,15 @@ export class ProjectManager { | |
if (cached) { | ||
return cached.data | ||
} else { | ||
const promise = this.sendRequest<OpenProject>('project/open', fullParams) | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
4e6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let promise: Promise<OpenProject> | ||
if (enableProjectService.value) { | ||
promise = this.runProjectServiceCommandJson('project/open', fullParams) | ||
} else { | ||
promise = this.sendRequest<OpenProject>('project/open', fullParams) | ||
} | ||
this.projects.set(fullParams.projectId, { | ||
state: backend.ProjectState.openInProgress, | ||
data: promise, | ||
|
@@ -190,11 +205,19 @@ export class ProjectManager { | |
} | ||
const fullParams: CloseProjectParams = this.paramsWithPathToWithId(params) | ||
this.projects.delete(fullParams.projectId) | ||
return this.sendRequest('project/close', fullParams) | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
if (enableProjectService.value) { | ||
return this.runProjectServiceCommandJson('project/close', fullParams) | ||
} else { | ||
return this.sendRequest('project/close', fullParams) | ||
} | ||
} | ||
|
||
/** Create a new project. */ | ||
async createProject(params: CreateProjectParams): Promise<CreateProject> { | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
let result: Omit<CreateProject, 'projectPath'> | ||
|
@@ -232,7 +255,14 @@ export class ProjectManager { | |
/** Rename a project. */ | ||
async renameProject(params: WithProjectPath<RenameProjectParams>): Promise<void> { | ||
const fullParams: RenameProjectParams = this.paramsWithPathToWithId(params) | ||
await this.sendRequest('project/rename', fullParams) | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
if (enableProjectService.value) { | ||
await this.runProjectServiceCommandJson('project/rename', fullParams) | ||
} else { | ||
await this.sendRequest('project/rename', fullParams) | ||
} | ||
const state = this.projects.get(fullParams.projectId) | ||
if (state?.state === backend.ProjectState.opened) { | ||
this.projects.set(fullParams.projectId, { | ||
|
@@ -255,10 +285,15 @@ export class ProjectManager { | |
params: WithProjectPath<DuplicateProjectParams>, | ||
): Promise<DuplicatedProject> { | ||
const fullParams: DuplicateProjectParams = this.paramsWithPathToWithId(params) | ||
const result = await this.sendRequest<Omit<DuplicatedProject, 'projectPath'>>( | ||
'project/duplicate', | ||
fullParams, | ||
) | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
let result: Omit<DuplicatedProject, 'projectPath'> | ||
if (enableProjectService.value) { | ||
result = await this.runProjectServiceCommandJson('project/duplicate', fullParams) | ||
} else { | ||
result = await this.sendRequest('project/duplicate', fullParams) | ||
} | ||
// Update `internalDirectories` by listing the project's parent directory, because the | ||
// directory name of the project is unknown. Deleting the directory is not an option because | ||
// that will prevent ALL descendants of the parent directory from being updated. | ||
|
@@ -279,7 +314,14 @@ export class ProjectManager { | |
if (cached && backend.IS_OPENING_OR_OPENED[cached.state]) { | ||
await this.closeProject({ projectPath: params.projectPath }) | ||
} | ||
await this.sendRequest('project/delete', fullParams) | ||
// This is a Vue function, not a React hook, so the React hooks rule doesn't apply | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const enableProjectService = useFeatureFlag('enableProjectService') | ||
if (enableProjectService.value) { | ||
await this.runProjectServiceCommandJson('project/delete', fullParams) | ||
} else { | ||
await this.sendRequest('project/delete', fullParams) | ||
} | ||
this.projectIds.delete(params.projectPath) | ||
this.projects.delete(fullParams.projectId) | ||
const siblings = this.directories.get(fullParams.projectsDirectory) | ||
|
@@ -466,6 +508,8 @@ export class ProjectManager { | |
|
||
/** Send a JSON-RPC request to the project manager. */ | ||
private async sendRequest<T = void>(method: string, params: unknown): Promise<T> { | ||
// Initialize socket lazily if not already initialized | ||
this.socketPromise ??= this.reconnect() | ||
const socket = await this.socketPromise | ||
const id = this.id++ | ||
socket.send(JSON.stringify({ jsonrpc: '2.0', id, method, params })) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/usr/bin/env node | ||
import { rmSync } from 'fs' | ||
import { dirname, join } from 'path' | ||
import process from 'process' | ||
import { fileURLToPath } from 'url' | ||
import { downloadEnsoEngine } from '../dist/projectService/ensoRunner.js' | ||
|
||
const __filename = fileURLToPath(import.meta.url) | ||
const __dirname = dirname(__filename) | ||
const projectRoot = join(__dirname, '../../..') | ||
const builtDistributionPath = join(projectRoot, 'built-distribution') | ||
|
||
// Delete the built-distribution directory | ||
rmSync(builtDistributionPath, { recursive: true, force: true }) | ||
|
||
// Download the engine | ||
downloadEnsoEngine(projectRoot) | ||
.then(() => { | ||
process.exit(0) | ||
}) | ||
.catch((error) => { | ||
console.error('Failed to download engine:', error) | ||
process.exit(1) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** | ||
* @file Re-exports for handler module. | ||
*/ | ||
|
||
export * from './filesystem.js' | ||
export * from './projectService.js' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a check if we're able to download engine, or we need to download engine for one of these checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need the enso executable to run the
ProjectService.test.ts
test suite