Skip to content

Commit 9506648

Browse files
authored
Merge pull request #363 from frictionlessdata/339-Create-upgrade_schema()
Create `upgrade_schema()`
2 parents 102c179 + ff68c3a commit 9506648

8 files changed

Lines changed: 189 additions & 29 deletions

File tree

R/upgrade_descriptor.R

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ upgrade_descriptor <- function(package) {
1919
"https://datapackage.org/profiles/2.0/datapackage.json"
2020

2121
# Set $schema to profile if URL (to custom profile)
22+
# https://datapackage.org/standard/data-package/#dollar-schema
2223
profile <- package$profile %||% "undefined"
2324
if (is_url(profile)) {
2425
purrr::pluck(package, "$schema") <- profile
@@ -34,23 +35,16 @@ upgrade_descriptor <- function(package) {
3435
package$profile <- NULL
3536

3637
# Update contributor "role" = "value" to "roles" = ["value"]
37-
contributors <- package$contributors
38-
if (!is.null(contributors)) {
39-
package$contributors <- purrr::map(
40-
contributors,
41-
function(contributor) {
42-
if ("role" %in% names(contributor)) {
43-
purrr::list_modify(
44-
contributor,
45-
roles = as.list(contributor$role), # Make array
46-
role = purrr::zap()
47-
)
48-
} else {
49-
contributor # Return as if contributor does not have role
50-
}
51-
}
38+
# https://datapackage.org/overview/changelog/#packagecontributors-updated
39+
package$contributors <- purrr::modify_if(
40+
package$contributors,
41+
function(x) "role" %in% names(x),
42+
function(x) purrr::list_modify(
43+
x,
44+
roles = character_to_list(x$role),
45+
role = purrr::zap()
5246
)
53-
}
47+
)
5448

5549
return(package)
5650
}

R/upgrade_resource.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ upgrade_resource <- function(resource) {
1616
purrr::pluck(resource, "$schema") <-
1717
"https://datapackage.org/profiles/2.0/dataresource.json"
1818

19-
# Set type to table if resource is tabular, see
19+
# Set type to table if resource is tabular
2020
# https://datapackage.org/standard/data-resource/#type
2121
profile <- resource$profile %||% "undefined"
2222
if (profile %in% c(

R/upgrade_schema.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#' Upgrade a Table Schema object from v1 to v2
2+
#'
3+
#' @param schema List describing a Table Schema, as returned by [schema()].
4+
#' @param resource_name Name of the Data Resource, needed for updating
5+
#' `foreignKeys`.
6+
#' @returns Upgraded `schema`.
7+
#' @family upgrade functions
8+
#' @noRd
9+
upgrade_schema <- function(schema, resource_name) {
10+
version <- version(schema)
11+
12+
# Leave schema as is if already 2.0 or higher
13+
if (version != "1.0") {
14+
return(schema)
15+
}
16+
17+
# Set $schema
18+
purrr::pluck(schema, "$schema") <-
19+
"https://datapackage.org/profiles/2.0/tableschema.json"
20+
21+
# Update primaryKey to array
22+
# https://datapackage.org/overview/changelog/#schemaprimarykey-updated
23+
schema$primaryKey <- character_to_list(schema$primaryKey)
24+
25+
# Update foreignKeys fields to array and remove self-referential resource
26+
# https://datapackage.org/overview/changelog/#schemaforeignkeys-updated
27+
schema$foreignKeys <- purrr::modify_if(
28+
schema$foreignKeys,
29+
is.list,
30+
function(x) {
31+
if (!is.null(purrr::pluck(x, "fields"))) {
32+
x$fields <- character_to_list(x$fields)
33+
}
34+
if (!is.null(purrr::pluck(x, "reference", "fields"))) {
35+
x$reference$fields <- character_to_list(x$reference$fields)
36+
}
37+
if (!is.null(purrr::pluck(x, "reference", "resource"))) {
38+
if (x$reference$resource == resource_name) {
39+
x$reference$resource <- NULL
40+
}
41+
}
42+
x
43+
}
44+
)
45+
46+
return(schema)
47+
}

R/utils.R

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ clean_list <- function(x, fun = is.null, recursive = FALSE) {
4343
"[<-"(x, vapply(x, fun, logical(1L)), NULL)
4444
}
4545

46+
#' Convert character to list
47+
#'
48+
#' @param x Value, e.g `"a"`.
49+
#' @returns List containing value (`list("a")`) if character, otherwise value.
50+
#' @family helper functions
51+
#' @noRd
52+
character_to_list <- function(x) {
53+
if (is.character(x)) as.list(x) else x
54+
}
55+
4656
#' Check if path is URL
4757
#'
4858
#' @param path Path.

tests/testthat/test-upgrade_descriptor.R

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,19 @@ test_that("upgrade_descriptor() sets $schema to default v2 value or custom
3030
p_v1 <- example_package(version = "1.0")
3131
v2_profile <- "https://datapackage.org/profiles/2.0/datapackage.json"
3232

33-
# Ignore undefined
33+
# Ignore undefined profile
3434
p_v1$profile <- NULL
3535
expect_identical(upgrade_descriptor(p_v1)$`$schema`, v2_profile)
3636

37-
# Ignore default value
37+
# Ignore default value profile
3838
p_v1$profile <- "data-package"
3939
expect_identical(upgrade_descriptor(p_v1)$`$schema`, v2_profile)
4040

41-
# Ignore tabular-data-package (deprecated)
41+
# Ignore tabular-data-package profile (deprecated)
4242
p_v1$profile <- "tabular-data-package"
4343
expect_identical(upgrade_descriptor(p_v1)$`$schema`, v2_profile)
4444

45-
# Keep fiscal-data-package (but set to URL)
45+
# Keep fiscal-data-package profile (but set to URL)
4646
p_v1$profile <- "fiscal-data-package"
4747
expect_identical(
4848
upgrade_descriptor(p_v1)$`$schema`,
@@ -54,7 +54,7 @@ test_that("upgrade_descriptor() sets $schema to default v2 value or custom
5454
p_v1$profile <- custom_profile_url
5555
expect_identical(upgrade_descriptor(p_v1)$`$schema`, custom_profile_url)
5656

57-
# Ignore unregistered value
57+
# Ignore unregistered value in profile
5858
p_v1$profile <- "unregistered-package"
5959
expect_identical(upgrade_descriptor(p_v1)$`$schema`, v2_profile)
6060
})
@@ -64,13 +64,14 @@ test_that("upgrade_descriptor() updates contributor role to roles", {
6464

6565
# Defined
6666
p_v1$contributors <- list(
67-
list(title = "First author", role = "author"),
68-
list(title = "No role", custom_property = "custom"),
67+
list(title = "First author", role = "author", custom = "custom"),
68+
list(title = "No role", custom = "custom"),
6969
list(title = "Jack of all trades", roles = list("a", "b"))
7070
)
7171
p_upgraded <- upgrade_descriptor(p_v1)
7272
expect_identical(p_upgraded$contributors[[1]][["roles"]], list("author")) # Added
7373
expect_null(p_upgraded$contributors[[1]][["role"]]) # Removed
74+
expect_identical(p_upgraded$contributors[[1]][["custom"]], "custom") # Kept
7475
expect_null(p_upgraded$contributors[[2]][["roles"]]) # Not added
7576
expect_identical(p_upgraded$contributors[[3]][["roles"]], list("a", "b")) # Kept
7677

tests/testthat/test-upgrade_resource.R

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Return ----
2-
test_that("upgrade_descriptor() returns resource object", {
2+
test_that("upgrade_descriptor() returns a resource object", {
33
resource_v1 <- resource(example_package(version = "1.0"), "deployments")
44
# Resource object has "data_location" attribute that should be kept
55
expect_identical(
@@ -33,23 +33,23 @@ test_that("upgrade_resource() sets $schema to default v2 value", {
3333
resource_v1 <- resource(example_package(version = "1.0"), "deployments")
3434
v2_profile <- "https://datapackage.org/profiles/2.0/dataresource.json"
3535

36-
# Ignore undefined
36+
# Ignore undefined profile
3737
resource_v1$profile <- NULL
3838
expect_identical(upgrade_resource(resource_v1)$`$schema`, v2_profile)
3939

40-
# Ignore default value
40+
# Ignore default value profile
4141
resource_v1$profile <- "data-resource"
4242
expect_identical(upgrade_resource(resource_v1)$`$schema`, v2_profile)
4343

44-
# Ignore tabular-data-resource (retained in type: table)
44+
# Ignore tabular-data-resource profile (retained in type: table)
4545
resource_v1$profile <- "tabular-data-resource"
4646
expect_identical(upgrade_resource(resource_v1)$`$schema`, v2_profile)
4747

4848
# Ignore URL to custom profile (hardly used)
4949
resource_v1$profile <- "http://example.com/my-profiles-json-schema.json"
5050
expect_identical(upgrade_resource(resource_v1)$`$schema`, v2_profile)
5151

52-
# Ignore unregistered value
52+
# Ignore unregistered value profile
5353
resource_v1$profile <- "unregistered-resource"
5454
expect_identical(upgrade_resource(resource_v1)$`$schema`, v2_profile)
5555
})
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Return ----
2+
test_that("upgrade_schema() returns a valid schema", {
3+
schema_v1 <- schema(example_package(version = "1.0"), "deployments")
4+
expect_no_error(check_schema(upgrade_schema(schema_v1, "deployments")))
5+
})
6+
7+
# Functionality ----
8+
test_that("upgrade_schema() upgrades a v1 schema to v2", {
9+
schema_v1 <- schema(example_package(version = "1.0"), "deployments")
10+
expect_identical(version(upgrade_schema(schema_v1, "deployments")), "2.0")
11+
})
12+
13+
test_that("upgrade_schema() leaves a v2 schema as is", {
14+
schema_v2 <- schema(example_package(version = "2.0"), "deployments")
15+
expect_identical(upgrade_schema(schema_v2, "deployments"), schema_v2)
16+
})
17+
18+
test_that("upgrade_schema() sets $schema to default v2 value", {
19+
schema_v1 <- schema(example_package(version = "1.0"), "deployments")
20+
expect_identical(
21+
upgrade_schema(schema_v1, "deployments")$`$schema`,
22+
"https://datapackage.org/profiles/2.0/tableschema.json"
23+
)
24+
})
25+
26+
test_that("upgrade_schema() updates primaryKey", {
27+
schema_v1 <- schema(example_package(version = "1.0"), "deployments")
28+
29+
# Update character
30+
schema_v1$primaryKey <- "deployment_id"
31+
expect_identical(
32+
upgrade_schema(schema_v1, "deployments")$primaryKey,
33+
list("deployment_id")
34+
)
35+
36+
# Leave undefined
37+
schema_v1$primaryKey <- NULL
38+
expect_null(upgrade_schema(schema_v1, "deployments")$primaryKey)
39+
40+
# Leave list
41+
schema_v1$primaryKey <- list("deployment_id", "other_id")
42+
expect_identical(
43+
upgrade_schema(schema_v1, "deployments")$primaryKey,
44+
list("deployment_id", "other_id")
45+
)
46+
})
47+
48+
test_that("upgrade_schema() updates foreignKeys", {
49+
schema_v1 <- schema(example_package(version = "1.0"), "media")
50+
# Media has fk to deployment.deployment_id and observations.observation_id
51+
52+
# v1 foreignKeys become v2
53+
expect_identical(
54+
upgrade_schema(schema_v1, "media")$foreignKeys,
55+
schema(example_package(version = "2.0"), "media")$foreignKeys,
56+
)
57+
58+
# Leave undefined
59+
schema_v1$foreignKeys <- NULL
60+
expect_null(upgrade_schema(schema_v1, "media")$foreignKeys)
61+
62+
# Extensive example
63+
provided_keys <- list(
64+
fields = "not_in_list", # Ignore
65+
list(
66+
fields = "media_id", # Update to list
67+
custom = "custom", # Ignore
68+
reference = list(
69+
resource = "media", # Self-referential, remove
70+
fields = "media_id", # Update to list
71+
custom = "custom" # Ignore
72+
)
73+
),
74+
list(
75+
custom = "custom", # Ignore
76+
reference = list(custom = "custom") # Ignore
77+
)
78+
)
79+
expected_keys <- list(
80+
fields = "not_in_list",
81+
list(
82+
fields = list("media_id"),
83+
custom = "custom",
84+
reference = list(
85+
fields = list("media_id"),
86+
custom = "custom"
87+
)
88+
),
89+
list(
90+
custom = "custom",
91+
reference = list(custom = "custom")
92+
)
93+
)
94+
schema_v1$foreignKeys <- provided_keys
95+
expect_identical(
96+
upgrade_schema(schema_v1, "media")$foreignKeys,
97+
expected_keys
98+
)
99+
})

tests/testthat/test-utils.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ test_that("clean_list() removes elements from list that match condition", {
4848
)
4949
})
5050

51+
# character_to_list() ----
52+
test_that("character_to_list() converts character to list", {
53+
expect_identical(character_to_list("a"), list("a")) # Update
54+
expect_identical(character_to_list(c("a", "b")), list("a", "b")) # Update
55+
expect_identical(character_to_list(list("a", "b")), list("a", "b")) # Keep
56+
expect_identical(character_to_list(1), 1) # Keep
57+
expect_null(character_to_list(NULL)) # Keep
58+
})
59+
5160
# is_url() ----
5261
test_that("is_url() tests whether path is URL", {
5362
expect_true(is_url("http://example.com"))

0 commit comments

Comments
 (0)