Skip to content

Commit

Permalink
Allow unauthenticated users to connect to an instance (#19)
Browse files Browse the repository at this point in the history
* Allow unauthenticated users to query an instance

* refactor connect function

* update changelog

* rename variables

* reduce code duplication

* update changelog
  • Loading branch information
rcannood authored Oct 9, 2024
1 parent 2277aea commit 0b426a9
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 75 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <instance>` beforehand (PR #19).

## MINOR CHANGES

* Update `README` with new set up instructions and simplify (PR #14).
Expand Down
74 changes: 20 additions & 54 deletions R/InstanceSettings.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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
Expand All @@ -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
}
)
)
72 changes: 72 additions & 0 deletions R/UserSettings.R
Original file line number Diff line number Diff line change
@@ -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
}
)
)
67 changes: 56 additions & 11 deletions R/connect.R
Original file line number Diff line number Diff line change
Expand Up @@ -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 <slug>`."
)
} else {
paste0(
"No default user or instance is loaded! Either:\n",
" - Call `lamin login` to set a default user.\n",
" - Call `lamin load <slug>` 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)
}
Expand Down
20 changes: 14 additions & 6 deletions R/settings_load.R
Original file line number Diff line number Diff line change
Expand Up @@ -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 <instance_id>` to load an instance.")
}
settings_store <-
tryCatch(
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
8 changes: 4 additions & 4 deletions R/settings_store.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 0b426a9

Please sign in to comment.