Skip to content

Commit

Permalink
Auto create the root folder if not present. (#10444)
Browse files Browse the repository at this point in the history
Adds `filesystem-exists` to Project Manager CLI.
If the root folder isn't present will add it at start up.

(cherry picked from commit 891f176)
  • Loading branch information
jdunkerley committed Jul 4, 2024
1 parent e7bc071 commit 468e60e
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 51 deletions.
111 changes: 60 additions & 51 deletions app/ide-desktop/lib/dashboard/src/services/LocalBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,58 +124,67 @@ export default class LocalBackend extends Backend {
): Promise<backend.AnyAsset[]> {
const parentIdRaw = query.parentId == null ? null : extractTypeAndId(query.parentId).id
const parentId = query.parentId ?? newDirectoryId(this.projectManager.rootDirectory)
const entries = await this.projectManager.listDirectory(parentIdRaw)
return entries
.map(entry => {
switch (entry.type) {
case projectManager.FileSystemEntryType.DirectoryEntry: {
return {
type: backend.AssetType.directory,
id: newDirectoryId(entry.path),
modifiedAt: entry.attributes.lastModifiedTime,
parentId,
title: fileInfo.fileName(entry.path),
permissions: [],
projectState: null,
labels: [],
description: null,
} satisfies backend.DirectoryAsset
}
case projectManager.FileSystemEntryType.ProjectEntry: {
return {
type: backend.AssetType.project,
id: newProjectId(entry.metadata.id),
title: entry.metadata.name,
modifiedAt: entry.metadata.lastOpened ?? entry.metadata.created,
parentId,
permissions: [],
projectState: {
type:
this.projectManager.projects.get(entry.metadata.id)?.state ??
backend.ProjectState.closed,
volumeId: '',
path: entry.path,
},
labels: [],
description: null,
} satisfies backend.ProjectAsset
}
case projectManager.FileSystemEntryType.FileEntry: {
return {
type: backend.AssetType.file,
id: newFileId(entry.path),
title: fileInfo.fileName(entry.path),
modifiedAt: entry.attributes.lastModifiedTime,
parentId,
permissions: [],
projectState: null,
labels: [],
description: null,
} satisfies backend.FileAsset
// Check if Root Directory Exists
if (
parentIdRaw == null &&
!(await this.projectManager.exists(this.projectManager.rootDirectory))
) {
await this.projectManager.createDirectory(this.projectManager.rootDirectory)
return []
} else {
const entries = await this.projectManager.listDirectory(parentIdRaw)
return entries
.map(entry => {
switch (entry.type) {
case projectManager.FileSystemEntryType.DirectoryEntry: {
return {
type: backend.AssetType.directory,
id: newDirectoryId(entry.path),
modifiedAt: entry.attributes.lastModifiedTime,
parentId,
title: fileInfo.fileName(entry.path),
permissions: [],
projectState: null,
labels: [],
description: null,
} satisfies backend.DirectoryAsset
}
case projectManager.FileSystemEntryType.ProjectEntry: {
return {
type: backend.AssetType.project,
id: newProjectId(entry.metadata.id),
title: entry.metadata.name,
modifiedAt: entry.metadata.lastOpened ?? entry.metadata.created,
parentId,
permissions: [],
projectState: {
type:
this.projectManager.projects.get(entry.metadata.id)?.state ??
backend.ProjectState.closed,
volumeId: '',
path: entry.path,
},
labels: [],
description: null,
} satisfies backend.ProjectAsset
}
case projectManager.FileSystemEntryType.FileEntry: {
return {
type: backend.AssetType.file,
id: newFileId(entry.path),
title: fileInfo.fileName(entry.path),
modifiedAt: entry.attributes.lastModifiedTime,
parentId,
permissions: [],
projectState: null,
labels: [],
description: null,
} satisfies backend.FileAsset
}
}
}
})
.sort(backend.compareAssets)
})
.sort(backend.compareAssets)
}
}

/** Return a list of projects belonging to the current user.
Expand Down
14 changes: 14 additions & 0 deletions app/ide-desktop/lib/dashboard/src/services/ProjectManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,20 @@ export default class ProjectManager {
return await this.sendRequest<VersionList>('engine/list-available', {})
}

/** Checks if a file or directory exists. */
async exists(parentId: Path | null) {
/** The type of the response body of this endpoint. */
interface ResponseBody {
readonly exists: boolean
}
const response = await this.runStandaloneCommand<ResponseBody>(
null,
'filesystem-exists',
parentId ?? this.rootDirectory
)
return response.exists
}

/** List directories, projects and files in the given folder. */
async listDirectory(parentId: Path | null) {
/** The type of the response body of this endpoint. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ export default function projectManagerShimMiddleware(
})
try {
switch (cliArguments[0]) {
case '--filesystem-exists': {
const directoryPath = cliArguments[1]
if (directoryPath != null) {
const exists = await fs
.access(directoryPath)
.then(() => true)
.catch(() => false)
result = toJSONRPCResult({ exists })
}
break
}
case '--filesystem-list': {
const directoryPath = cliArguments[1]
if (directoryPath != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object Cli {
val PROJECTS_DIRECTORY = "projects-directory"
val PROJECT_LIST = "project-list"

val FILESYSTEM_EXISTS = "filesystem-exists"
val FILESYSTEM_LIST = "filesystem-list"
val FILESYSTEM_CREATE_DIRECTORY = "filesystem-create-directory"
val FILESYSTEM_DELETE = "filesystem-delete"
Expand Down Expand Up @@ -90,6 +91,14 @@ object Cli {
.desc("List user projects.")
.build()

val filesystemExists: cli.Option = cli.Option.builder
.hasArg(true)
.numberOfArgs(1)
.argName("path")
.longOpt(FILESYSTEM_EXISTS)
.desc("Check if a file or directory exists.")
.build()

val filesystemList: cli.Option = cli.Option.builder
.hasArg(true)
.numberOfArgs(1)
Expand Down Expand Up @@ -150,6 +159,7 @@ object Cli {
.addOption(option.profilingTime)
.addOption(option.projectsDirectory)
.addOption(option.projectList)
.addOption(option.filesystemExists)
.addOption(option.filesystemList)
.addOption(option.filesystemCreateDirectory)
.addOption(option.filesystemDelete)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.enso.projectmanager.boot.Globals.{
import org.enso.projectmanager.boot.command.filesystem.{
FileSystemCreateDirectoryCommand,
FileSystemDeleteCommand,
FileSystemExistsCommand,
FileSystemListCommand,
FileSystemMoveDirectoryCommand,
FileSystemWritePathCommand
Expand Down Expand Up @@ -218,6 +219,11 @@ object ProjectManager extends ZIOAppDefault with LazyLogging {
ZIO.succeed(SuccessExitCode)
} else if (options.hasOption(Cli.VERSION_OPTION)) {
displayVersion(options.hasOption(Cli.JSON_OPTION))
} else if (options.hasOption(Cli.FILESYSTEM_EXISTS)) {
val path = Paths.get(options.getOptionValue(Cli.FILESYSTEM_EXISTS))
val fileSystemExistsCommand =
FileSystemExistsCommand[ZIO[ZAny, +*, +*]](config, path.toFile)
commandHandler.printJson(fileSystemExistsCommand.run)
} else if (options.hasOption(Cli.FILESYSTEM_LIST)) {
val directory = Paths.get(options.getOptionValue(Cli.FILESYSTEM_LIST))
val fileSystemListCommand =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.enso.projectmanager.boot.command.filesystem

import org.enso.projectmanager.boot.configuration.ProjectManagerConfig
import org.enso.projectmanager.control.core.syntax._
import org.enso.projectmanager.control.core.{Applicative, CovariantFlatMap}
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
import org.enso.projectmanager.infrastructure.file.BlockingFileSystem
import org.enso.projectmanager.infrastructure.random.SystemGenerator
import org.enso.projectmanager.infrastructure.repository.ProjectFileRepositoryFactory
import org.enso.projectmanager.infrastructure.time.RealClock
import org.enso.projectmanager.protocol.FileSystemManagementApi.FileSystemExists
import org.enso.projectmanager.service.filesystem.{
FileSystemService,
FileSystemServiceApi,
FileSystemServiceFailure
}

import java.io.File

final class FileSystemExistsCommand[
F[+_, +_]: CovariantFlatMap
](service: FileSystemServiceApi[F], path: File) {

def run: F[FileSystemServiceFailure, FileSystemExists.Result] =
service
.exists(path)
.map(FileSystemExists.Result)
}

object FileSystemExistsCommand {

def apply[F[+_, +_]: Applicative: CovariantFlatMap: ErrorChannel: Sync](
config: ProjectManagerConfig,
path: File
): FileSystemExistsCommand[F] = {
val clock = new RealClock[F]
val fileSystem = new BlockingFileSystem[F](config.timeout.ioTimeout)
val gen = new SystemGenerator[F]
val projectRepositoryFactory = new ProjectFileRepositoryFactory[F](
config.storage,
clock,
fileSystem,
gen
)

val service = new FileSystemService[F](fileSystem, projectRepositoryFactory)

new FileSystemExistsCommand[F](service, path)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ object FileSystemManagementApi {
}
}

case object FileSystemExists extends Method("filesystem/exists") {

case class Params(path: File)

case class Result(exists: Boolean)

implicit val hasParams: HasParams.Aux[this.type, FileSystemExists.Params] =
new HasParams[this.type] {
type Params = FileSystemExists.Params
}

implicit val hasResult: HasResult.Aux[this.type, FileSystemExists.Result] =
new HasResult[this.type] {
type Result = FileSystemExists.Result
}
}

case object FileSystemCreateDirectory
extends Method("filesystem/createDirectory") {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ object JsonRpc {
.registerRequest(ConfigDelete)
.registerRequest(LoggingServiceGetEndpoint)
.registerRequest(FileSystemList)
.registerRequest(FileSystemExists)
.registerRequest(FileSystemCreateDirectory)
.registerRequest(FileSystemDeleteDirectory)
.registerRequest(FileSystemMoveDirectory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ class FileSystemService[F[+_, +_]: Applicative: CovariantFlatMap: ErrorChannel](
projectRepositoryFactory: ProjectRepositoryFactory[F]
) extends FileSystemServiceApi[F] {

/** @inheritdoc */
override def exists(path: File): F[FileSystemServiceFailure, Boolean] =
fileSystem
.exists(path)
.mapError(_ =>
FileSystemServiceFailure.FileSystem("Failed to check if path exists")
)

/** @inheritdoc */
override def list(
path: File
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import java.io.{File, InputStream}

trait FileSystemServiceApi[F[+_, +_]] {

/** Checks if the file or directory exists.
*
* @param path the file or directory to check
* @return true if the file or directory exists, false otherwise
*/
def exists(path: File): F[FileSystemServiceFailure, Boolean]

/** List file system entries in the provided directory
*
* @param path the directory to list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,29 @@ class FileSystemServiceSpec
FileUtils.deleteQuietly(filePath)
}

"check existence of a path" in {
implicit val client: WsTestClient = new WsTestClient(address)

val testDir = testStorageConfig.userProjectsPath

val projectName = "New_Project_1"
createProject(projectName)

val testFile = new File(testDir, "foo.txt")
val dummyFile = new File(testDir, "foo.exe")

Files.createFile(testFile.toPath)

val result1 = fileSystemService
.exists(testFile)
.unsafeRunSync()
result1.value should be(true)

val result2 = fileSystemService
.exists(dummyFile)
.unsafeRunSync()
result2.value should be(false)
}

}
}

0 comments on commit 468e60e

Please sign in to comment.