2016-03-09 11 views
0

Я пытаюсь использовать пакет RSTudio/Hadley Wickham 'httr' R для возврата всех записей из запроса GET Okta API ('List Users Assigned to Application'). Следующий запрос отлично работает, чтобы получить максимальный предел записей (500) за вызов:Использование R и httr для извлечения данных - Okta API GET-запрос с заголовками с разбивкой по страницам

oktaurl <- "https://mydomain.okta.com/api/v1/apps/applicationID/users?limit=500" 

oktagetjson <- with_verbose(content(GET(oktaurl, 
             add_headers("Authorization" = "bearer myapikey", 
                 "Content-Type" = "application/json;charset=UTF-8")))) 

Разбор «oktagetjson» возвращаемых данных в работоспособное кадра данных с «jsonlite» и R не является проблемой; однако этот конкретный вызов API трудно ограничить максимум 500 записями на звонок, поэтому мне нужно как-то извлечь и разбивать страницы на все заголовки «Ссылка:», чтобы получить все несколько тысяч записей. 'Ссылка:' заголовки сами по себе в виде:

Link: <https://mydomain.okta.com/api/v1/apps/applicationID/users?limit=500>; rel="self" 

Link: <https://mydomain.okta.com/api/v1/apps/applicationID/users?after=random cursor string&limit=500>; rel="next" 

(документация Okta API описывает их структуру постраничной here)

Я застрял здесь:

  1. Я могу видеть первые два заголовка «Ссылка:», перечисленные выше в консоли R/RStudio при выполнении вызова «oktagetjson < - with_verbose (контент (GET (oktaurl и т. д. ...) ...) ', чтобы получить мой объект oktagetjston, но' Ссылка: «заголовки не возвращаются как часть самого объекта. headers(HEAD("https://mydomain.okta.com/api/v1/apps/<applicationID>/users")) возвращает некоторые заголовки, но не возвращает постраничной «Ссылка:» заголовки
  2. надписью «Ссылка:» заголовки содержат случайные строки курсора, так что я не могу угадать их фактический формат
  3. Даже если бы я мог получить всю необходимую «Ссылка: ', я понятия не имею, как вызывать/итерации/paginate/рекурсивно следовать через все из них в R, чтобы построить объект всего набора данных из нескольких тысяч записей.

К сожалению, из-за характера запроса, поставщика услуг и данных я не могу представить полностью воспроизводимый пример с реальными ссылками и образцами данных, но я надеюсь, что концепция достаточно ясна, чтобы кто-то указал мне в правильном направлении - даже если это направление не должно использовать пакет «httr» или R для этого усилия.

Благодарим вас за рассмотрение.

+0

Похоже, что они предоставляют вам новый URL для следующих 500 пользователей. Вы пробовали использовать его? – cory

+0

Im работает над своего рода DSL для выполнения http-запросов, которые обертывают 'httr', см. Эту проблему https://github.com/sckott/request/issues/2 для текущей работы, которая отлично справится с вашим случаем использования – sckott

+0

@cory - Да, «следующий» URL-адрес может быть проанализирован из возвращаемого заголовка из GET-запроса, и он работает, чтобы получить следующий набор записей. Теперь мне нужно выяснить, как рекурсивно получить все «следующие» URL-адреса и передать их в «контент» (GET), чтобы получить все записи. Вот где я застрял в данный момент. – Gratalis

ответ

0

взломали что-то вместе некоторое время назад, что работает, но, безусловно, не выиграет ни одной награды за элегантность. Измените его, чтобы пользователи также назначали приложения Okta. Полезно, если вы проверяете/присоединяетесь к другим данным компании/каталога.

library(jsonlite) 
library(dplyr) 
library(httr) 
library(purrr) 
library(stringi) 
library(tidyr) 

# create character vector to hold URLs we'll use later when we GET content 
url_list <- as.character() 

# list placeholder for GET content 
okta_content <- list() 

# initial URL construction parts for first URL 
okta_urllimit = as.character("200") 
okta_baseurl <- paste0("https://<your company>.okta.com/api/v1/users?limit=",okta_urllimit) 

# next URL construction parts for 'next' URLs 
basenexturl <- "https://<your company>.okta.com/api/v1/users?after=" 
baselimiturl <- "&limit=200" 

# Pass initial URL to get first batch 
okta_get01 <- httr::GET(okta_baseurl, 
         config = (
          add_headers(Authorization = "SSWS <your Okta API key>"))) 


# append the URL vector 
url_list <- append(url_list, okta_baseurl) 


# unlist the all_headers list element from the URL 
testallheaders <- as.character(unlist(okta_get01$all_headers)) 

okta_content <- append(okta_content,content(okta_get01)) 

# if "next" is in the second link URL (testallheaders[16]) then iterate for as long as 
# the next URL header element has "next" in it 

while (
    grepl("next",testallheaders[16]) == 'TRUE' 
) 

{ 
    # parse the sha value 
    testparsenext <- regmatches(testallheaders[16], gregexpr('(?<=after=).*?(?=&limit)',testallheaders[16], perl=T))[[1]] 
    # and create URL 
    oktaurlnext <- paste0(basenexturl,testparsenext,baselimiturl) 


    # iterate and replace 'okta_baseurl' with each subsquent oktaurlnext 

    okta_get01 <- httr::GET(oktaurlnext, 
          config = (
          add_headers(Authorization = "SSWS <your Okta API key>"))) 

    testallheaders <- as.character(unlist(okta_get01$all_headers)) 
    url_list <- append(url_list, oktaurlnext) 
    okta_content <- append(okta_content,content(okta_get01)) 

    next 
} 


# Parse the results into something usable 

oktagettojson <- toJSON(okta_content, simplifyDataFrame = TRUE, flatten = TRUE, recursive = TRUE) 
oktagetdf <- fromJSON(oktagettojson, simplifyDataFrame = TRUE, flatten = TRUE) 
dfnames <- names(oktagetdf) 
oktagetdf <- oktagetdf %>% map_if(is.list, as.character) 
oktagetdf <- do.call(cbind, lapply(oktagetdf, data.frame, stringsAsFactors=FALSE)) 
names(oktagetdf) <- dfnames 

# adding columns to separate AD domain mastered account and domain names 
oktagetdf <- separate(oktagetdf, profile.login, 
         into = c("credPrefix", "credSuffix"), sep = "@", remove = FALSE, extra = "drop") 

# select some data frame columns of interest 
okta_allusers <- subset(oktagetdf, select = c("id","status","created","lastLogin","profile.login","credPrefix", "credSuffix","profile.firstName","profile.lastName","profile.email","credentials.provider.type","credentials.provider.name"))