From ea311ebe0358e8723206bad9c1b26b9353b80111 Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 18:51:05 +0300 Subject: [PATCH 1/6] feature: add endpoint class and generics - All endpoints, such as Rhea and Uniprot will implement these generics - We using S7 OOP system in this pacjkage - Every Endpoint will keep three dataframes, for properties, classes and the links between them --- DESCRIPTION | 5 ++- R/endpoint_class.R | 77 ++++++++++++++++++++++++++++++++++++++++++++++ R/zzz.R | 5 +++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 R/endpoint_class.R create mode 100644 R/zzz.R diff --git a/DESCRIPTION b/DESCRIPTION index ed8120d..7272859 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,12 +13,15 @@ Description: The main goal is to connect to SPARQL endpoints and bering their st License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 LazyData: true Imports: + S7, tools, httr2, stats Suggests: testthat (>= 3.0.0) Config/testthat/edition: 3 +Depends: + R (>= 4.3) +Config/roxygen2/version: 8.0.0 diff --git a/R/endpoint_class.R b/R/endpoint_class.R new file mode 100644 index 0000000..72a39b5 --- /dev/null +++ b/R/endpoint_class.R @@ -0,0 +1,77 @@ +#' 'SPARQL' endpoint (base class) +#' +#' The base S7 class every endpoint backend inherits from. You will +#' not normally call `sparql_endpoint()` directly -- use a specific +#' constructor like [new_generic_endpoint()]. +#' +#' @param url Character. The SPARQL endpoint URL (the address a query +#' is sent to). +#' @param name Character. A short label for the endpoint, used for +#' caching and messages. +#' @param headers A named list of extra HTTP headers sent with every +#' request (for example a custom `User-Agent`). +#' @param timeout Numeric. Request timeout in seconds. +#' +#' @export +sparql_endpoint <- S7::new_class( + "sparql_endpoint", + properties = list( + url = S7::new_property( + S7::class_character, + validator = function(value) { + if (length(value) != 1 || !nzchar(value)) "must be a single non-empty string" + } + ), + name = S7::new_property( + S7::class_character, + default = quote(NA_character_), + validator = function(value) { + if (length(value) != 1) "must be a single string (or NA)" + } + ), + headers = S7::new_property( + S7::class_list, + default = quote(list()), + validator = function(value) { + if (length(value) > 0 && (is.null(names(value)) || any(!nzchar(names(value))))) { + "must be a fully named list" + } + } + ), + timeout = S7::new_property( + S7::class_numeric, + default = 60, + validator = function(value) { + if (length(value) != 1 || is.na(value) || value <= 0) "must be a single positive number" + } + ) + ) +) + +#' Generic 'SPARQL' 1.1 backend +#' +#' The fallback backend used for any endpoint that isn't registered +#' under a more specific class. +#' +#' @param url,name,headers,timeout See [sparql_endpoint()]. +#' @export +generic_endpoint <- S7::new_class("generic_endpoint", parent = sparql_endpoint) + +#' Discover the classes used by an endpoint +#' @param endpoint A `sparql_endpoint` object. +#' @param ... Passed on to methods (commonly `limit`). +#' @return A data frame with columns `class` and `n`. +#' @export +ep_classes <- S7::new_generic("ep_classes", "endpoint") + +#' Discover the properties (predicates) used by an endpoint +#' @inheritParams ep_classes +#' @return A data frame with columns `property` and `n`. +#' @export +ep_properties <- S7::new_generic("ep_properties", "endpoint") + +#' Discover which classes are linked by which properties +#' @inheritParams ep_classes +#' @return A data frame with columns `from_class`, `property`, `to_class`, `n`. +#' @export +ep_class_links <- S7::new_generic("ep_class_links", "endpoint") diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..dfe2495 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,5 @@ +NULL + +.onLoad <- function(libname, pkgname) { + S7::methods_register() +} From c72ca17bf7b556813b551f8aea2131d302296d7b Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 19:16:42 +0300 Subject: [PATCH 2/6] ci: add develop branch to ci cycle --- .github/workflows/R-CMD-check.yaml | 100 ++++++++++++++--------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 73bfb21..86c1b42 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -1,50 +1,50 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - branches: [main, master] - pull_request: - -name: R-CMD-check.yaml - -permissions: read-all - -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: macos-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - - steps: - - uses: actions/checkout@v6 - - - uses: r-lib/actions/setup-pandoc@v2 - - - uses: r-lib/actions/setup-r@v2 - with: - r-version: ${{ matrix.config.r }} - http-user-agent: ${{ matrix.config.http-user-agent }} - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::rcmdcheck - needs: check - - - uses: r-lib/actions/check-r-package@v2 - with: - upload-snapshots: true - build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master, develop] + pull_request: + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v6 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' From dac5c4b5398577b4d02e9a1bb18c737146fe372e Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 19:26:14 +0300 Subject: [PATCH 3/6] doc: add roxygen documentation --- man/ep_class_links.Rd | 19 +++++++++++++++++++ man/ep_classes.Rd | 19 +++++++++++++++++++ man/ep_properties.Rd | 19 +++++++++++++++++++ man/generic_endpoint.Rd | 20 ++++++++++++++++++++ man/sparql_endpoint.Rd | 30 ++++++++++++++++++++++++++++++ man/sparql_query.Rd | 21 +++++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 man/ep_class_links.Rd create mode 100644 man/ep_classes.Rd create mode 100644 man/ep_properties.Rd create mode 100644 man/generic_endpoint.Rd create mode 100644 man/sparql_endpoint.Rd create mode 100644 man/sparql_query.Rd diff --git a/man/ep_class_links.Rd b/man/ep_class_links.Rd new file mode 100644 index 0000000..645c6e3 --- /dev/null +++ b/man/ep_class_links.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/endpoint_class.R +\name{ep_class_links} +\alias{ep_class_links} +\title{Discover which classes are linked by which properties} +\usage{ +ep_class_links(endpoint, ...) +} +\arguments{ +\item{endpoint}{A \code{sparql_endpoint} object.} + +\item{...}{Passed on to methods (commonly \code{limit}).} +} +\value{ +A data frame with columns \code{from_class}, \code{property}, \code{to_class}, \code{n}. +} +\description{ +Discover which classes are linked by which properties +} diff --git a/man/ep_classes.Rd b/man/ep_classes.Rd new file mode 100644 index 0000000..a39c005 --- /dev/null +++ b/man/ep_classes.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/endpoint_class.R +\name{ep_classes} +\alias{ep_classes} +\title{Discover the classes used by an endpoint} +\usage{ +ep_classes(endpoint, ...) +} +\arguments{ +\item{endpoint}{A \code{sparql_endpoint} object.} + +\item{...}{Passed on to methods (commonly \code{limit}).} +} +\value{ +A data frame with columns \code{class} and \code{n}. +} +\description{ +Discover the classes used by an endpoint +} diff --git a/man/ep_properties.Rd b/man/ep_properties.Rd new file mode 100644 index 0000000..a1cd2d5 --- /dev/null +++ b/man/ep_properties.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/endpoint_class.R +\name{ep_properties} +\alias{ep_properties} +\title{Discover the properties (predicates) used by an endpoint} +\usage{ +ep_properties(endpoint, ...) +} +\arguments{ +\item{endpoint}{A \code{sparql_endpoint} object.} + +\item{...}{Passed on to methods (commonly \code{limit}).} +} +\value{ +A data frame with columns \code{property} and \code{n}. +} +\description{ +Discover the properties (predicates) used by an endpoint +} diff --git a/man/generic_endpoint.Rd b/man/generic_endpoint.Rd new file mode 100644 index 0000000..8a1c04d --- /dev/null +++ b/man/generic_endpoint.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/endpoint_class.R +\name{generic_endpoint} +\alias{generic_endpoint} +\title{Generic 'SPARQL' 1.1 backend} +\usage{ +generic_endpoint( + url = character(0), + name = NA_character_, + headers = list(), + timeout = 60 +) +} +\arguments{ +\item{url, name, headers, timeout}{See \code{\link[=sparql_endpoint]{sparql_endpoint()}}.} +} +\description{ +The fallback backend used for any endpoint that isn't registered +under a more specific class. +} diff --git a/man/sparql_endpoint.Rd b/man/sparql_endpoint.Rd new file mode 100644 index 0000000..56b40a9 --- /dev/null +++ b/man/sparql_endpoint.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/endpoint_class.R +\name{sparql_endpoint} +\alias{sparql_endpoint} +\title{'SPARQL' endpoint (base class)} +\usage{ +sparql_endpoint( + url = character(0), + name = NA_character_, + headers = list(), + timeout = 60 +) +} +\arguments{ +\item{url}{Character. The SPARQL endpoint URL (the address a query +is sent to).} + +\item{name}{Character. A short label for the endpoint, used for +caching and messages.} + +\item{headers}{A named list of extra HTTP headers sent with every +request (for example a custom \code{User-Agent}).} + +\item{timeout}{Numeric. Request timeout in seconds.} +} +\description{ +The base S7 class every endpoint backend inherits from. You will +not normally call \code{sparql_endpoint()} directly -- use a specific +constructor like \code{\link[=new_generic_endpoint]{new_generic_endpoint()}}. +} diff --git a/man/sparql_query.Rd b/man/sparql_query.Rd new file mode 100644 index 0000000..affcf9a --- /dev/null +++ b/man/sparql_query.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/transport.R +\name{sparql_query} +\alias{sparql_query} +\title{Send 'SPARQL' query against an endpoint through HTTP request.} +\usage{ +sparql_query(url, query, timeout = 60) +} +\arguments{ +\item{url}{Character scalar. The SPARQL endpoint URL.} + +\item{query}{Character scalar. A SPARQL query.} + +\item{timeout}{Numeric scalar. Timeout in seconds.} +} +\value{ +The parsed response in Json Format. +} +\description{ +Send 'SPARQL' query against an endpoint through HTTP request. +} From 1f12b117aa9b13af019a72b45079fb448b9af203 Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 19:26:56 +0300 Subject: [PATCH 4/6] test: add tests for endpoint_class generics --- tests/testthat/test-endpoint_class.R | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/testthat/test-endpoint_class.R diff --git a/tests/testthat/test-endpoint_class.R b/tests/testthat/test-endpoint_class.R new file mode 100644 index 0000000..fdc8877 --- /dev/null +++ b/tests/testthat/test-endpoint_class.R @@ -0,0 +1,4 @@ +test_that("sparql_endpoint validator rejects a bad url", { + expect_error(sparql_endpoint(url = "")) + expect_error(sparql_endpoint(url = c("a", "b"))) +}) From f6f95b5bfd5734fd37a82c04c2579d10a791cc51 Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 19:50:22 +0300 Subject: [PATCH 5/6] doc: fix git merge conflicts within the file --- man/sparql_query.Rd | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/man/sparql_query.Rd b/man/sparql_query.Rd index 5b2eb70..affcf9a 100644 --- a/man/sparql_query.Rd +++ b/man/sparql_query.Rd @@ -1,20 +1,13 @@ -<<<<<<< HEAD % Generated by roxygen2: do not edit by hand % Please edit documentation in R/transport.R \name{sparql_query} \alias{sparql_query} \title{Send 'SPARQL' query against an endpoint through HTTP request.} -======= -\name{sparql_query} -\alias{sparql_query} -\title{Send a SPARQL Query} ->>>>>>> 015165589fa525d44ce108c1da6996a2c333b790 \usage{ sparql_query(url, query, timeout = 60) } \arguments{ \item{url}{Character scalar. The SPARQL endpoint URL.} -<<<<<<< HEAD \item{query}{Character scalar. A SPARQL query.} @@ -26,10 +19,3 @@ The parsed response in Json Format. \description{ Send 'SPARQL' query against an endpoint through HTTP request. } -======= -\item{query}{Character scalar. A SPARQL query.} -\item{timeout}{Numeric scalar. Timeout in seconds.} -} -\value{A data frame containing the parsed SPARQL response.} -\description{Send a SPARQL query against an endpoint through an HTTP request.} ->>>>>>> 015165589fa525d44ce108c1da6996a2c333b790 From e3da7a03da881e3949c8566df683df5514bb5fba Mon Sep 17 00:00:00 2001 From: Ata B Barzegar Date: Thu, 23 Jul 2026 20:02:50 +0300 Subject: [PATCH 6/6] doc: fix bracket bug in documents - A wrong link was created, which removed. --- R/endpoint_class.R | 2 +- man/sparql_endpoint.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/endpoint_class.R b/R/endpoint_class.R index 72a39b5..f0c8e06 100644 --- a/R/endpoint_class.R +++ b/R/endpoint_class.R @@ -2,7 +2,7 @@ #' #' The base S7 class every endpoint backend inherits from. You will #' not normally call `sparql_endpoint()` directly -- use a specific -#' constructor like [new_generic_endpoint()]. +#' constructor like `new_generic_endpoint()`. #' #' @param url Character. The SPARQL endpoint URL (the address a query #' is sent to). diff --git a/man/sparql_endpoint.Rd b/man/sparql_endpoint.Rd index 56b40a9..10ba0a1 100644 --- a/man/sparql_endpoint.Rd +++ b/man/sparql_endpoint.Rd @@ -26,5 +26,5 @@ request (for example a custom \code{User-Agent}).} \description{ The base S7 class every endpoint backend inherits from. You will not normally call \code{sparql_endpoint()} directly -- use a specific -constructor like \code{\link[=new_generic_endpoint]{new_generic_endpoint()}}. +constructor like \code{new_generic_endpoint()}. }