Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions R/crosswalks.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ onet_crosswalk_military <- function(keyword, start = 1, end = 20) {
}

resp <- onet_request("online/crosswalks/military",
keyword = keyword, start = start, end = end) |>
.query = list(keyword = keyword, start = start, end = end)) |>
onet_perform()

# Define expected schema
Expand Down Expand Up @@ -83,15 +83,14 @@ onet_taxonomy_map <- function(code, from = c("active", "2010"), to = c("2010", "
}

# Build the endpoint path based on direction
endpoint <- if (from == "active") {
paste0("taxonomy/active/2010/", code)
if (from == "active") {
resp <- onet_request("taxonomy/active/2010", .path_segments = code) |>
onet_perform()
} else {
paste0("taxonomy/2010/active/", code)
resp <- onet_request("taxonomy/2010/active", .path_segments = code) |>
onet_perform()
}

resp <- onet_request(endpoint) |>
onet_perform()

# Define expected schema
schema <- empty_tibble(code = character(), title = character())

Expand Down
11 changes: 6 additions & 5 deletions R/database.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ onet_tables <- function() {
# Define expected schema
schema <- empty_tibble(id = character(), title = character())

if (is.null(resp$table) || length(resp$table) == 0) {
# The API returns tables as a top-level list, not nested under 'table'
if (!is.list(resp) || length(resp) == 0) {
return(schema)
}

map(resp$table, \(x) {
map(resp, \(x) {
tibble(
id = x$id %||% NA_character_,
id = x$table_id %||% NA_character_,
title = x$title %||% NA_character_
)
}) |> list_rbind()
Expand Down Expand Up @@ -57,7 +58,7 @@ onet_table_info <- function(table_id) {
cli_abort("{.arg table_id} must be a single character string.")
}

resp <- onet_request("database/info", table_id) |>
resp <- onet_request("database/info", .path_segments = table_id) |>
onet_perform()

# Define expected schema
Expand Down Expand Up @@ -136,7 +137,7 @@ onet_table <- function(table_id, page_size = 2000, show_progress = TRUE) {
#' @return A list with `data` (tibble), `start`, `end`, and `total`.
#' @keywords internal
onet_table_page <- function(table_id, start = 1, end = 2000) {
resp <- onet_request("database/rows", table_id, start = start, end = end) |>
resp <- onet_request("database/rows", .path_segments = table_id, .query = list(start = start, end = end)) |>
onet_perform()

data <- if (is.null(resp$row) || length(resp$row) == 0) {
Expand Down
10 changes: 5 additions & 5 deletions R/occupations.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#' head(occupations)
#' }
onet_occupations <- function(start = 1, end = 1000) {
resp <- onet_request("online/occupations", start = start, end = end) |>
resp <- onet_request("online/occupations", .query = list(start = start, end = end)) |>
onet_perform()

# Define expected schema
Expand Down Expand Up @@ -51,7 +51,7 @@ onet_occupations <- function(start = 1, end = 1000) {
#' }
onet_occupation <- function(code) {
validate_onet_code(code)
resp <- onet_request("online/occupations", code, "summary") |>
resp <- onet_request("online/occupations", .path_segments = c(code, "summary")) |>
onet_perform()
resp
}
Expand All @@ -71,7 +71,7 @@ onet_occupation <- function(code) {
#' }
onet_occupation_details <- function(code) {
validate_onet_code(code)
resp <- onet_request("online/occupations", code, "details") |>
resp <- onet_request("online/occupations", .path_segments = c(code, "details")) |>
onet_perform()
resp
}
Expand Down Expand Up @@ -154,7 +154,7 @@ onet_abilities <- function(code) {
#' }
onet_technology <- function(code) {
validate_onet_code(code)
resp <- onet_request("online/occupations", code, "hot_technology") |>
resp <- onet_request("online/occupations", .path_segments = c(code, "hot_technology")) |>
onet_perform()

# Define expected schema
Expand Down Expand Up @@ -194,7 +194,7 @@ onet_occupation_element <- function(code, element) {
validate_onet_code(code)

# Get summary which contains all elements
resp <- onet_request("online/occupations", code, "summary") |>
resp <- onet_request("online/occupations", .path_segments = c(code, "summary")) |>
onet_perform()

# Define expected schema
Expand Down
24 changes: 19 additions & 5 deletions R/request.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,28 @@ onet_base_url <- "https://api-v2.onetcenter.org"
#' Creates an httr2 request object configured for the O*NET API.
#'
#' @param .path Character string specifying the API endpoint path.
#' @param ... Additional path segments and query parameters passed to the API.
#' @param .path_segments Additional path segments to append to the URL path (optional).
#' @param .query Named list or arguments for query parameters (optional).
#'
#' @return An httr2 request object.
#' @keywords internal
onet_request <- function(.path, ...) {
request(onet_base_url) |>
req_url_path_append(.path) |>
req_url_query(...) |>
onet_request <- function(.path, .path_segments = NULL, .query = list()) {
req <- request(onet_base_url) |>
req_url_path_append(.path)

# Append additional path segments if provided
if (!is.null(.path_segments)) {
for (segment in .path_segments) {
req <- req |> req_url_path_append(segment)
}
}

# Add query parameters if provided
if (length(.query) > 0) {
req <- req |> req_url_query(!!!.query)
}

req |>
req_headers(`X-API-Key` = onet_api_key()) |>
req_retry(
max_tries = 3,
Expand Down
11 changes: 9 additions & 2 deletions R/search.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ onet_search <- function(keyword, start = 1, end = 20) {
cli_abort("{.arg keyword} must be a single character string.")
}

resp <- onet_request("online/search", keyword = keyword, start = start, end = end) |>
resp <- onet_request("online/search", .query = list(keyword = keyword, start = start, end = end)) |>
onet_perform()

# Define expected schema for empty results
Expand All @@ -44,10 +44,17 @@ onet_search <- function(keyword, start = 1, end = 20) {
}

results <- map(resp$occupation, \(x) {
# Try multiple field names for relevance score
relevance <- x$relevance_score %||%
x$relevanceScore %||%
x$relevance %||%
x$score %||%
NA_real_

tibble(
code = x$code %||% NA_character_,
title = x$title %||% NA_character_,
relevance_score = x$relevance_score %||% NA_real_
relevance_score = relevance
)
}) |> list_rbind()

Expand Down
68 changes: 68 additions & 0 deletions tests/testthat/test-request-construction.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
test_that("onet_request builds URL correctly with path only", {
# This test verifies that onet_request can handle path-only endpoints
# without query parameters (which was causing errors before)

# Mock the API key
old_key <- Sys.getenv("ONET_API_KEY")
on.exit(Sys.setenv(ONET_API_KEY = old_key))
Sys.setenv(ONET_API_KEY = "test-key")

# Build a request with only path
req <- onet2r:::onet_request("database")

expect_s3_class(req, "httr2_request")
expect_match(req$url, "https://api-v2.onetcenter.org/database")
})

test_that("onet_request builds URL correctly with path segments", {
old_key <- Sys.getenv("ONET_API_KEY")
on.exit(Sys.setenv(ONET_API_KEY = old_key))
Sys.setenv(ONET_API_KEY = "test-key")

# Build a request with path segments
req <- onet2r:::onet_request("online/occupations", .path_segments = c("15-1252.00", "summary"))

expect_s3_class(req, "httr2_request")
expect_match(req$url, "https://api-v2.onetcenter.org/online/occupations/15-1252.00/summary")
})

test_that("onet_request builds URL correctly with query parameters", {
old_key <- Sys.getenv("ONET_API_KEY")
on.exit(Sys.setenv(ONET_API_KEY = old_key))
Sys.setenv(ONET_API_KEY = "test-key")

# Build a request with query parameters
req <- onet2r:::onet_request("online/search", .query = list(keyword = "software", start = 1, end = 20))

expect_s3_class(req, "httr2_request")
expect_match(req$url, "https://api-v2.onetcenter.org/online/search")
# The query params should be in the URL
expect_match(req$url, "keyword=software")
expect_match(req$url, "start=1")
expect_match(req$url, "end=20")
})

test_that("onet_request builds URL correctly with both path segments and query parameters", {
old_key <- Sys.getenv("ONET_API_KEY")
on.exit(Sys.setenv(ONET_API_KEY = old_key))
Sys.setenv(ONET_API_KEY = "test-key")

# Build a request with both path segments and query parameters
req <- onet2r:::onet_request("database/rows", .path_segments = "skills", .query = list(start = 1, end = 100))

expect_s3_class(req, "httr2_request")
expect_match(req$url, "https://api-v2.onetcenter.org/database/rows/skills")
expect_match(req$url, "start=1")
expect_match(req$url, "end=100")
})

test_that("onet_request includes API key header", {
old_key <- Sys.getenv("ONET_API_KEY")
on.exit(Sys.setenv(ONET_API_KEY = old_key))
test_key <- "test-api-key-123"
Sys.setenv(ONET_API_KEY = test_key)

req <- onet2r:::onet_request("database")

expect_equal(req$headers[["X-API-Key"]], test_key)
})