diff --git a/CHANGELOG.md b/CHANGELOG.md index 63b0ba0..36d02f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ - Linting action. - Commands for roxygenizing (`/document`) and restyling the source code (`/style`). +* Allow unauthenticated users to connect to an instance if they ran `lamin load ` beforehand (PR #19). + ## MINOR CHANGES * Update `README` with new set up instructions and simplify (PR #14). diff --git a/R/InstanceSettings.R b/R/InstanceSettings.R index 3345b99..abf45c2 100644 --- a/R/InstanceSettings.R +++ b/R/InstanceSettings.R @@ -11,31 +11,37 @@ InstanceSettings <- R6::R6Class( # nolint object_name_linter public = list( #' @param settings A named list of settings for the instance initialize = function(settings) { - expected_columns <- c( + expected_keys <- c( "owner", "name", "id", - "lnid", "schema_str", "schema_id", "git_repo", "keep_artifacts_local", - "api_url", - "lamindb_version", - "storage", - "db_scheme", - "db_host", - "db_port", - "db_database", - "db_permissions", - "db_user_name", - "db_user_password" + "api_url" ) - missing_column <- setdiff(expected_columns, names(settings)) + optional_keys <- c( + "lnid", # api + "lamindb_version", # api + "storage", # api + "storage_root", # lamin-cli + "storage_region", # lamin-cli + "db", # lamin-cli + "db_scheme", # api + "db_host", # api + "db_port", # api + "db_database", # api + "db_permissions", # api + "db_user_name", # api + "db_user_password", # api + "lamindb_version" # api + ) + missing_column <- setdiff(expected_keys, names(settings)) if (length(missing_column) > 0) { cli_abort("Missing column: ", missing_column) } - unexpected_columns <- setdiff(names(settings), expected_columns) + unexpected_columns <- setdiff(names(settings), c(expected_keys, optional_keys)) if (length(unexpected_columns) > 0) { cli_abort("Unexpected column: ", unexpected_columns) } @@ -58,10 +64,6 @@ InstanceSettings <- R6::R6Class( # nolint object_name_linter id = function() { private$.settings$id }, - #' Get the LNID of the instance. - lnid = function() { - private$.settings$lnid - }, #' Get the schema string of the instance. schema_str = function() { private$.settings$schema_str @@ -81,42 +83,6 @@ InstanceSettings <- R6::R6Class( # nolint object_name_linter #' Get the API URL of the instance. api_url = function() { private$.settings$api_url - }, - #' Get the LaminDB version of the instance. - lamindb_version = function() { - private$.settings$lamindb_version - }, - #' Get the storage of the instance. - storage = function() { - private$.settings$storage - }, - #' Get the database scheme of the instance. - db_scheme = function() { - private$.settings$db_scheme - }, - #' Get the database host of the instance. - db_host = function() { - private$.settings$db_host - }, - #' Get the database port of the instance. - db_port = function() { - private$.settings$db_port - }, - #' Get the database of the instance. - db_database = function() { - private$.settings$db_database - }, - #' Get the database permissions of the instance. - db_permissions = function() { - private$.settings$db_permissions - }, - #' Get the database user name of the instance. - db_user_name = function() { - private$.settings$db_user_name - }, - #' Get the database user password of the instance. - db_user_password = function() { - private$.settings$db_user_password } ) ) diff --git a/R/UserSettings.R b/R/UserSettings.R new file mode 100644 index 0000000..35238f7 --- /dev/null +++ b/R/UserSettings.R @@ -0,0 +1,72 @@ +#' @title UserSettings +#' +#' @noRd +#' +#' @description +#' Settings for a Lamin user. These settings are retrieved from the +#' Lamin Hub API and are used to connect to the user. +UserSettings <- R6::R6Class( # nolint object_name_linter + "UserSettings", + cloneable = FALSE, + public = list( + #' @param settings A named list of settings for the user + initialize = function(settings) { + expected_keys <- c( + "email", + "password", + "access_token", + "api_key", + "uid", + "uuid", + "handle", + "name" + ) + missing_column <- setdiff(expected_keys, names(settings)) + if (length(missing_column) > 0) { + cli_abort("Missing column: ", missing_column) + } + unexpected_columns <- setdiff(names(settings), expected_keys) + if (length(unexpected_columns) > 0) { + cli_abort("Unexpected column: ", unexpected_columns) + } + private$.settings <- settings + } + ), + private = list( + .settings = NULL + ), + active = list( + # Get the email of the user. + email = function() { + private$.settings$email + }, + # Get the password of the user. + password = function() { + private$.settings$password + }, + # Get the access token of the user. + access_token = function() { + private$.settings$access_token + }, + # Get the API key of the user. + api_key = function() { + private$.settings$api_key + }, + # Get the UID of the user. + uid = function() { + private$.settings$uid + }, + # Get the UUID of the user. + uuid = function() { + private$.settings$uuid + }, + # Get the handle of the user. + handle = function() { + private$.settings$handle + }, + # Get the name of the user. + name = function() { + private$.settings$name + } + ) +) diff --git a/R/connect.R b/R/connect.R index 5b48c57..893a9cf 100644 --- a/R/connect.R +++ b/R/connect.R @@ -20,20 +20,65 @@ #' instance #' } connect <- function(slug = NULL) { - user_settings <- .settings_load__load_or_create_user_settings() + instance_file <- + if (is.null(slug)) { + # if the slug is null, see if we can load the default instance + .settings_store__current_instance_settings_file() + } else { + # if the slug is not null, try to load the instance from the local settings + owner_name <- .connect_get_owner_name_from_identifier(slug) - if (is.null(slug)) { - current_instance <- .settings_load__load_instance_settings() - slug <- paste0(current_instance$owner, "/", current_instance$name) - } + .settings_store__instance_settings_file( + name = owner_name$name, + owner = owner_name$owner + ) + } - owner_name <- .connect_get_owner_name_from_identifier(slug) + instance_settings <- + if (file.exists(instance_file)) { + .settings_load__load_instance_settings(instance_file) + } else { + # try to load the user settings from the api + user_file <- .settings_store__current_user_settings_file() - instance_settings <- .connect_get_instance_settings( - owner = owner_name$owner, - name = owner_name$name, - access_token = user_settings$access_token - ) + if (!file.exists(user_file) || is.null(slug)) { + error_msg <- + if (is.null(slug)) { + paste0( + "Could not load default instance. Either:\n", + " - Provide a slug. For example: `connect(\"laminlabs/cellxgene\")`)\n", + " - Set a default instance by running `lamin load `." + ) + } else { + paste0( + "No default user or instance is loaded! Either:\n", + " - Call `lamin login` to set a default user.\n", + " - Call `lamin load ` to set a default instance." + ) + } + cli_abort(error_msg) + } else { + user_settings <- .settings_load__load_user_settings(user_file) + + owner_name <- .connect_get_owner_name_from_identifier(slug) + + .connect_get_instance_settings( + owner = owner_name$owner, + name = owner_name$name, + access_token = user_settings$access_token + ) + } + } + + for (required_field in c("id", "api_url", "schema_id")) { + if (is.null(instance_settings[[required_field]])) { + cli_abort(paste0( + "Invalid instance settings: missing field '", required_field, "'\n", + "Your instance settings file is likely outdated. Please update lamin-cli,\n", + "delete the instance settings file, and reload the instance." + )) + } + } create_instance(instance_settings = instance_settings) } diff --git a/R/settings_load.R b/R/settings_load.R index 0554401..de8a027 100644 --- a/R/settings_load.R +++ b/R/settings_load.R @@ -9,7 +9,7 @@ instance_settings_file <- .settings_store__current_instance_settings_file() } if (!file.exists(instance_settings_file)) { - cli_abort("No instance is loaded! Call `lamin init` or `lamin load`") + cli_abort("No instance is loaded! Call `lamin load ` to load an instance.") } settings_store <- tryCatch( @@ -38,7 +38,17 @@ if (!file.exists(file)) { cli_warn("using anonymous user (to identify, call `lamin login`)") - cli_abort("TODO: implement this") + content <- list( + email = NULL, + password = NULL, + access_token = NULL, + api_key = NULL, + uid = NULL, + uuid = NULL, + handle = "anonymous", + name = NULL + ) + .settings_load__setup_user_from_store(content) } else { .settings_load__load_user_settings(file) } @@ -61,11 +71,9 @@ } .settings_load__setup_instance_from_store <- function(store) { # nolint object_length_linter - # TODO: implement this? - return(store) + InstanceSettings$new(store) } .settings_load__setup_user_from_store <- function(store) { # nolint object_length_linter - # TODO: implement this? - return(store) + UserSettings$new(store) } diff --git a/R/settings_store.R b/R/settings_store.R index 04a0254..928ce40 100644 --- a/R/settings_store.R +++ b/R/settings_store.R @@ -124,14 +124,14 @@ api_url = "Optional[str]", owner = "str", name = "str", - storage_root = "str", - storage_region = "str", - db = "Optional[str]", schema_str = "Optional[str]", schema_id = "Optional[str]", id = "str", git_repo = "Optional[str]", - keep_artifacts_local = "Optional[bool]" + keep_artifacts_local = "Optional[bool]", + storage_root = "Optional[str]", + storage_region = "Optional[str]", + db = "Optional[str]" ) .settings_store__read_typed_env(env_file, env_prefix, field_types)