-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
#' Get the Metadata Document from the OData Dataset Service. | ||
#' | ||
#' `r lifecycle::badge("experimental")` | ||
#' | ||
#' The Metadata Document describes, in | ||
#' [EDMX CSDL](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html), | ||
#' the schema of all the data you can retrieve from the OData Dataset Service | ||
#' in question. Essentially, these are the Dataset properties, or the schema of | ||
#' each Entity, translated into the OData format. | ||
#' | ||
#' @template tpl-structure-nested | ||
#' @template tpl-def-entitylist | ||
#' @template tpl-entitylist-dataset | ||
#' @template tpl-auth-missing | ||
#' @template tpl-compat-2022-3 | ||
#' @template param-pid | ||
#' @template param-did | ||
#' @template param-url | ||
#' @template param-auth | ||
#' @template param-retries | ||
#' @template param-odkcv | ||
#' @template param-orders | ||
#' @template param-tz | ||
#' @return An S3 class `odata_entitylist_metadata_get` and `list` containing | ||
#' the Metadata document following the DDMX CSDL standard | ||
#' | ||
#' * `version` The EDMX version, e.g. "4.0" | ||
#' * `complex_types` | ||
#' * `entity_types` | ||
#' * `containers` | ||
# nolint start | ||
#' @seealso \url{https://docs.getodk.org/central-api-odata-endpoints/#id2} | ||
#' @seealso \url{https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html} | ||
# nolint end | ||
#' @family entity-management | ||
#' @export | ||
#' @examples | ||
#' \dontrun{ | ||
#' # See vignette("setup") for setup and authentication options | ||
#' # ruODK::ru_setup(svc = "....svc", un = "[email protected]", pw = "...") | ||
#' | ||
#' ds <- entitylist_list(pid = get_default_pid()) | ||
#' | ||
#' dm1 <- odata_entitylist_metadata_get(pid = get_default_pid(), did = ds$name[1]) | ||
#' | ||
#' # Overview | ||
#' print(dm1) | ||
#' | ||
#' # Get all property names for an entity type | ||
#' names(dm1$entity_types$Entities$properties) | ||
#' | ||
#' # Check what properties are non-filterable | ||
#' dm1$containers$trees$entity_sets$Entities$capabilities | ||
#' | ||
#' # Get complex type definitions | ||
#' dm1$complex_types$metadata$properties | ||
#' } | ||
odata_entitylist_metadata_get <- function(pid = get_default_pid(), | ||
did = "", | ||
url = get_default_url(), | ||
un = get_default_un(), | ||
pw = get_default_pw(), | ||
retries = get_retries(), | ||
odkc_version = get_default_odkc_version(), | ||
orders = get_default_orders(), | ||
tz = get_default_tz()) { | ||
yell_if_missing(url, un, pw, pid = pid, did = did) | ||
|
||
if (odkc_version |> semver_lt("2022.3")) { | ||
ru_msg_warn("odata_entitylist_service_get is supported from v2022.3") | ||
} | ||
|
||
doc <- httr::RETRY( | ||
"GET", | ||
httr::modify_url(url, | ||
path = glue::glue( | ||
"v1/projects/{pid}/datasets/", | ||
"{URLencode(did, reserved = TRUE)}.svc/$metadata" | ||
) | ||
), | ||
httr::add_headers( | ||
"Accept" = "application/xml" | ||
), | ||
httr::authenticate(un, pw), | ||
times = retries | ||
) |> | ||
yell_if_error(url, un, pw) |> | ||
httr::content(encoding = "utf-8") | ||
|
||
# Convert EDMX XML Document to structured R object | ||
|
||
# Define the namespaces explicitly | ||
ns <- c( | ||
edmx = "http://docs.oasis-open.org/odata/ns/edmx", | ||
edm = "http://docs.oasis-open.org/odata/ns/edm" | ||
) | ||
|
||
# Helper function to extract property information | ||
extract_properties <- function(type_node) { | ||
props <- xml2::xml_find_all(type_node, "./edm:Property", ns) | ||
property_list <- lapply(props, function(prop) { | ||
list( | ||
name = xml2::xml_attr(prop, "Name"), | ||
type = xml2::xml_attr(prop, "Type") | ||
) | ||
}) | ||
names(property_list) <- sapply(property_list, `[[`, "name") | ||
property_list | ||
} | ||
|
||
# Extract complex types | ||
complex_types <- xml2::xml_find_all(doc, "//edm:ComplexType", ns) | ||
complex_types_list <- lapply(complex_types, function(type) { | ||
list( | ||
name = xml2::xml_attr(type, "Name"), | ||
properties = extract_properties(type) | ||
) | ||
}) | ||
names(complex_types_list) <- sapply(complex_types_list, `[[`, "name") | ||
|
||
# Extract entity types | ||
entity_types <- xml2::xml_find_all(doc, "//edm:EntityType", ns) | ||
entity_types_list <- lapply(entity_types, function(type) { | ||
# Extract key properties | ||
keys <- xml2::xml_find_all(type, ".//edm:PropertyRef", ns) | ||
key_names <- sapply(keys, xml2::xml_attr, "Name") | ||
|
||
list( | ||
name = xml2::xml_attr(type, "Name"), | ||
keys = key_names, | ||
properties = extract_properties(type) | ||
) | ||
}) | ||
names(entity_types_list) <- sapply(entity_types_list, `[[`, "name") | ||
|
||
# Extract entity container information | ||
containers <- xml2::xml_find_all(doc, "//edm:EntityContainer", ns) | ||
container_list <- lapply(containers, function(container) { | ||
entity_sets <- xml2::xml_find_all(container, "./edm:EntitySet", ns) | ||
sets_list <- lapply(entity_sets, function(set) { | ||
# Extract capabilities from annotations | ||
annotations <- xml2::xml_find_all(set, "./edm:Annotation", ns) | ||
capabilities <- lapply(annotations, function(anno) { | ||
term <- xml2::xml_attr(anno, "Term") | ||
if (grepl("ConformanceLevel$", term)) { | ||
list( | ||
capability = "conformance_level", | ||
value = xml2::xml_attr(anno, "EnumMember") | ||
) | ||
} else if (grepl("BatchSupported$", term)) { | ||
list( | ||
capability = "batch_supported", | ||
value = as.logical(xml2::xml_attr(anno, "Bool")) | ||
) | ||
} else if (grepl("FilterRestrictions$", term)) { | ||
# Extract non-filterable properties | ||
non_filterable <- xml2::xml_find_all(anno, ".//edm:PropertyPath", ns) | ||
if (length(non_filterable) > 0) { | ||
list( | ||
capability = "filter_restrictions", | ||
non_filterable_properties = xml2::xml_text(non_filterable) | ||
) | ||
} else { | ||
NULL | ||
} | ||
} else { | ||
NULL | ||
} | ||
}) | ||
capabilities <- Filter(Negate(is.null), capabilities) | ||
|
||
list( | ||
name = xml2::xml_attr(set, "Name"), | ||
type = xml2::xml_attr(set, "EntityType"), | ||
capabilities = capabilities | ||
) | ||
}) | ||
names(sets_list) <- sapply(sets_list, `[[`, "name") | ||
|
||
list( | ||
name = xml2::xml_attr(container, "Name"), | ||
entity_sets = sets_list | ||
) | ||
}) | ||
names(container_list) <- sapply(container_list, `[[`, "name") | ||
|
||
# Get the root Edmx node to extract version | ||
edmx_root <- xml2::xml_find_first(doc, "//edmx:Edmx", ns) | ||
version <- xml2::xml_attr(edmx_root, "Version") | ||
|
||
# Create the final structure | ||
structure(list( | ||
version = version, | ||
complex_types = complex_types_list, | ||
entity_types = entity_types_list, | ||
containers = container_list | ||
), class = c("odata_entitylist_metadata_get", "list")) | ||
} | ||
|
||
#' @export | ||
print.odata_entitylist_metadata_get <- function(x, ...) { | ||
cat("<ruODK OData EntityList Metadata>", sep = "\n") | ||
cat(" Complex Types: ") | ||
print(names(x$complex_types)) | ||
cat(" Entity Types: ") | ||
print(names(x$entity_types)) | ||
cat(" Containers: ") | ||
print(names(x$containers)) | ||
} | ||
|
||
|
||
# usethis::use_test("odata_entitylist_metadata_get") # nolint |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.