Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shared Dimensions: searching and paging #1565

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions .changeset/late-cows-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cube-creator/ui": minor
"@cube-creator/shared-dimensions-api": minor
---

Searching and paging Shared Dimensions
2 changes: 1 addition & 1 deletion apis/shared-dimensions/bootstrap/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import type { BootstrappedResourceFactory } from './index'

export const entrypoint = (ptr: BootstrappedResourceFactory, ns: NamespaceBuilder) =>
ptr('').addOut(rdf.type, [hydra.Resource, md.Entrypoint])
.addOut(md.sharedDimensions, ns('_term-sets?pageSize=1000'))
.addOut(md.sharedDimensions, ns('_term-sets?pageSize=20'))
.addOut(md.hierarchies, ns('_hierarchies'))
5 changes: 5 additions & 0 deletions apis/shared-dimensions/bootstrap/shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const SharedDimensionUpdate = clownface({ dataset: $rdf.dataset() })
.namedNode(shape('shape/shared-dimension-update'))
.addOut(rdf.type, sh.NodeShape)

const SharedDimensionSearch = clownface({ dataset: $rdf.dataset() })
.namedNode(shape('shape/shared-dimension-search'))
.addOut(rdf.type, sh.NodeShape)

const SharedDimensionTermCreate = clownface({ dataset: $rdf.dataset() })
.namedNode(shape('shape/shared-dimension-term-create'))
.addOut(rdf.type, sh.NodeShape)
Expand All @@ -30,6 +34,7 @@ const HierarchyCreate = clownface({ dataset: $rdf.dataset() })
export default [
SharedDimensionCreate,
SharedDimensionUpdate,
SharedDimensionSearch,
SharedDimensionTermCreate,
SharedDimensionTermUpdate,
Hierarchy,
Expand Down
13 changes: 11 additions & 2 deletions apis/shared-dimensions/hydra/index.ttl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
PREFIX owl: <http://www.w3.org/2002/07/owl#>
BASE <urn:hydra-box:api>
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema: <http://schema.org/> .
Expand Down Expand Up @@ -41,8 +42,10 @@ md:SharedDimensions
rdfs:subClassOf hydra:Collection ;
hydra:supportedOperation
[
a hydra:Operation ;
a hydra:Operation, schema:DownloadAction ;
hydra:method "GET" ;
hydra:title "Search" ;
hydra:expects <dimension/_shape/shared-dimension-search> ;
code:implementedBy
[
a code:EcmaScript ;
Expand All @@ -51,7 +54,7 @@ md:SharedDimensions
hydra-box:variables
[
a hydra:IriTemplate ;
hydra:template "/_term-sets{?q,pageSize,page}" ;
hydra:template "/_term-sets{?q,pageSize,page,includeDeprecated}" ;
hydra:mapping
[
a hydra:IriTemplateMapping ;
Expand All @@ -67,6 +70,11 @@ md:SharedDimensions
a hydra:IriTemplateMapping ;
hydra:property hydra:pageIndex ;
hydra:variable "page" ;
],
[
a hydra:IriTemplateMapping ;
hydra:property owl:deprecated ;
hydra:variable "includeDeprecated" ;
] ;
] ;
], [
Expand All @@ -85,6 +93,7 @@ md:SharedDimensions

<dimension/_shape/shared-dimension-create> a sh:NodeShape, sh:Shape .
<dimension/_shape/shared-dimension-update> a sh:NodeShape, sh:Shape .
<dimension/_shape/shared-dimension-search> a sh:NodeShape, sh:Shape .

md:Hierarchies
a hydra:Class ;
Expand Down
15 changes: 10 additions & 5 deletions apis/shared-dimensions/lib/domain/hierarchies.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NamedNode } from '@rdfjs/types'
import type { NamedNode, Quad } from '@rdfjs/types'

Check warning on line 1 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L1

Added line #L1 was not covered by tests
import { CONSTRUCT, SELECT } from '@tpluscode/sparql-builder'
import { md, meta } from '@cube-creator/core/namespace'
import clownface, { GraphPointer } from 'clownface'
Expand All @@ -7,9 +7,11 @@
import $rdf from 'rdf-ext'
import slugify from 'slugify'
import { DomainError } from '@cube-creator/api-errors'
import { ParsingClient } from 'sparql-http-client/ParsingClient'

Check warning on line 10 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L10

Added line #L10 was not covered by tests
import { SharedDimensionsStore } from '../store'
import env from '../env'
import { textSearch } from '../query'
import { CollectionData } from '../handlers/collection'

Check warning on line 14 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L14

Added line #L14 was not covered by tests
import { newId, replace } from './resource'

interface GetHierarchies {
Expand All @@ -18,7 +20,7 @@
offset: number
}

export function getHierarchies({ freetextQuery, limit, offset }: GetHierarchies) {
export async function getHierarchies({ freetextQuery, limit, offset }: GetHierarchies, client: ParsingClient): Promise<CollectionData<Iterable<Quad>>> {

Check warning on line 23 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L23

Added line #L23 was not covered by tests
const hierarchy = $rdf.variable('hierarchy')
const name = $rdf.variable('name')

Expand All @@ -35,18 +37,21 @@
.ORDER().BY(name)
}

return CONSTRUCT`
return {
members: await CONSTRUCT`

Check warning on line 41 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L40-L41

Added lines #L40 - L41 were not covered by tests
?proxyUrl ?p ?o .
`
.WHERE`
.WHERE`

Check warning on line 44 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L44

Added line #L44 was not covered by tests
{
${select}
}

${hierarchy} ?p ?o .

BIND(IRI(CONCAT("${env.MANAGED_DIMENSIONS_API_BASE}", "dimension/_hierarchy/proxy?id=", ENCODE_FOR_URI(STR(${hierarchy})))) AS ?proxyUrl)
`
`.execute(client.query),
totalItems: 0,
}

Check warning on line 54 in apis/shared-dimensions/lib/domain/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/domain/hierarchies.ts#L52-L54

Added lines #L52 - L54 were not covered by tests
}

interface CreateHierarchy {
Expand Down
40 changes: 27 additions & 13 deletions apis/shared-dimensions/lib/domain/shared-dimensions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import path from 'path'
import type { Quad, Stream, Term } from '@rdfjs/types'
import type { Quad, Stream, Term, Literal, NamedNode } from '@rdfjs/types'
import { hydra, rdf, schema, sh } from '@tpluscode/rdf-ns-builders'
import $rdf from 'rdf-ext'
import { toRdf } from 'rdf-literal'
import { toRdf, fromRdf } from 'rdf-literal'
import { fromFile } from 'rdf-utils-fs'
import clownface from 'clownface'
import { isResource } from 'is-graph-pointer'
Expand All @@ -11,38 +11,50 @@ import { ParsingClient } from 'sparql-http-client/ParsingClient'
import { md } from '@cube-creator/core/namespace'
import env from '../env'
import shapeToQuery, { rewriteTemplates } from '../shapeToQuery'
import { CollectionData } from '../handlers/collection'
import { getDynamicProperties } from './shared-dimension'

interface GetSharedDimensions {
freetextQuery?: string
limit?: number
offset?: number
includeDeprecated?: Literal
}

export async function getSharedDimensions(client: StreamClient, { freetextQuery = '', limit = 10, offset = 0 }: GetSharedDimensions = {}): Promise<Quad[]> {
export async function getSharedDimensions(client: StreamClient, { freetextQuery = '', limit = 10, offset = 0, includeDeprecated }: GetSharedDimensions = {}): Promise<CollectionData<Iterable<Quad>>> {
const { constructQuery } = await shapeToQuery()

const shape = await loadShape('dimensions-query-shape')
const memberQueryShape = await loadShape('dimensions-query-shape', md.MembersQueryShape)
const totalQueryShape = await loadShape('dimensions-query-shape', md.CountQueryShape)

const { MANAGED_DIMENSIONS_BASE } = env
const variables = new Map(Object.entries({
MANAGED_DIMENSIONS_BASE,
limit,
offset,
freetextQuery,
includeDeprecated,
orderBy: schema.name,
}))
await rewriteTemplates(shape, variables)
await rewriteTemplates(memberQueryShape, variables)
await rewriteTemplates(totalQueryShape, variables)

const dataset = await $rdf.dataset().import(await constructQuery(shape).execute(client))
const dataset = await $rdf.dataset().import(await client.query.construct(constructQuery(memberQueryShape)))
clownface({ dataset })
.has(rdf.type, schema.DefinedTermSet)
.forEach(termSet => {
termSet.addOut(md.export, $rdf.namedNode(`${MANAGED_DIMENSIONS_BASE}dimension/_export?dimension=${termSet.value}`))
termSet.addOut(md.terms, $rdf.namedNode(`${MANAGED_DIMENSIONS_BASE}dimension/_terms?dimension=${termSet.value}`))
})

return dataset.toArray()
const totalItems = clownface({
dataset: await $rdf.dataset().import(await client.query.construct(constructQuery(totalQueryShape))),
}).has(hydra.totalItems).out(hydra.totalItems).term as Literal

return {
members: dataset,
totalItems: fromRdf(totalItems),
}
}

interface GetSharedTerms {
Expand All @@ -53,7 +65,7 @@ interface GetSharedTerms {
validThrough?: Date
}

export async function getSharedTerms<C extends StreamClient | ParsingClient>({ sharedDimensions, freetextQuery, validThrough, limit = 10, offset = 0 }: GetSharedTerms, client: C): Promise<C extends StreamClient ? Stream : Quad[]> {
export async function getSharedTerms<C extends StreamClient | ParsingClient>({ sharedDimensions, freetextQuery, validThrough, limit = 10, offset = 0 }: GetSharedTerms, client: C): Promise<CollectionData<C extends StreamClient ? Stream : Quad[]>> {
const shape = await loadShape('terms-query-shape')

shape.addOut(sh.targetNode, sharedDimensions)
Expand Down Expand Up @@ -86,17 +98,19 @@ export async function getSharedTerms<C extends StreamClient | ParsingClient>({ s
}

const { constructQuery } = await shapeToQuery()
return constructQuery(shape).execute(client, {
operation: 'postDirect',
}) as any
return {
members: await client.query.construct(constructQuery(shape), {
operation: 'postDirect',
}) as any,
}
}

async function loadShape(shape: string) {
async function loadShape(shape: string, shapeType: NamedNode = sh.NodeShape) {
const dataset = await $rdf.dataset().import(fromFile(path.resolve(__dirname, `../shapes/${shape}.ttl`)))

const ptr = clownface({
dataset,
}).has(rdf.type, sh.NodeShape)
}).has(rdf.type, shapeType)

if (!isResource(ptr)) {
throw new Error('Expected a single blank node or named node')
Expand Down
20 changes: 15 additions & 5 deletions apis/shared-dimensions/lib/handlers/collection.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import type { NamedNode, Quad } from '@rdfjs/types'
import type { NamedNode, Quad, Stream } from '@rdfjs/types'

Check warning on line 1 in apis/shared-dimensions/lib/handlers/collection.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/collection.ts#L1

Added line #L1 was not covered by tests
import $rdf from 'rdf-ext'
import clownface, { GraphPointer } from 'clownface'
import { hydra, rdf } from '@tpluscode/rdf-ns-builders'

export interface CollectionData<M extends Stream | Iterable<Quad> = Stream | Iterable<Quad>> {
members: M
totalItems?: number
}

Check warning on line 10 in apis/shared-dimensions/lib/handlers/collection.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/collection.ts#L6-L10

Added lines #L6 - L10 were not covered by tests
interface CollectionHandler {
memberType: NamedNode
collectionType: NamedNode
view?: NamedNode
memberQuads: Quad[]
data: CollectionData<Iterable<Quad>>

Check warning on line 15 in apis/shared-dimensions/lib/handlers/collection.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/collection.ts#L15

Added line #L15 was not covered by tests
collection: NamedNode
}

export function getCollection({ collection, view, memberQuads, memberType, collectionType }: CollectionHandler): GraphPointer<NamedNode> {
const dataset = $rdf.dataset(memberQuads)
export function getCollection({ collection, view, data: { members: memberQuads, totalItems }, memberType, collectionType }: CollectionHandler): GraphPointer<NamedNode> {
const dataset = $rdf.dataset([...memberQuads])

Check warning on line 20 in apis/shared-dimensions/lib/handlers/collection.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/collection.ts#L19-L20

Added lines #L19 - L20 were not covered by tests

const graph = clownface({ dataset })
const members = graph.has(rdf.type, memberType)

graph.node(collection)
.addOut(rdf.type, [hydra.Collection, collectionType])
.addOut(hydra.member, members)
.addOut(hydra.totalItems, members.terms.length)

if (totalItems) {
graph.node(collection).addOut(hydra.totalItems, totalItems)
} else {
graph.node(collection).addOut(hydra.totalItems, members.terms.length)
}

Check warning on line 33 in apis/shared-dimensions/lib/handlers/collection.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/collection.ts#L28-L33

Added lines #L28 - L33 were not covered by tests

if (view) {
graph.node(view)
Expand Down
2 changes: 1 addition & 1 deletion apis/shared-dimensions/lib/handlers/hierarchies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
offset,
}
const collection = await getCollection({
memberQuads: await getHierarchies(queryParams).execute(parsingClient.query),
data: await getHierarchies(queryParams, parsingClient),

Check warning on line 30 in apis/shared-dimensions/lib/handlers/hierarchies.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/hierarchies.ts#L30

Added line #L30 was not covered by tests
collectionType: md.Hierarchies,
memberType: md.Hierarchy,
collection: req.hydra.resource.term,
Expand Down
2 changes: 1 addition & 1 deletion apis/shared-dimensions/lib/handlers/hierarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
})

const hierarchy = clownface({
dataset: $rdf.dataset(await query.execute(parsingClient)),
dataset: $rdf.dataset(await parsingClient.query.construct(query)),

Check warning on line 59 in apis/shared-dimensions/lib/handlers/hierarchy.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/hierarchy.ts#L59

Added line #L59 was not covered by tests
}).namedNode(url)
ensureEndpoint(hierarchy)

Expand Down
27 changes: 17 additions & 10 deletions apis/shared-dimensions/lib/handlers/shared-dimensions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Term } from '@rdfjs/types'
import { hydra, oa, schema } from '@tpluscode/rdf-ns-builders'
import { hydra, oa, owl, schema, xsd } from '@tpluscode/rdf-ns-builders'

Check warning on line 2 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L2

Added line #L2 was not covered by tests
import { asyncMiddleware } from 'middleware-async'
import { protectedResource } from '@hydrofoil/labyrinth/resource'
import { Enrichment } from '@hydrofoil/labyrinth/lib/middleware/preprocessResource'
import httpError from 'http-errors'
import clownface, { GraphPointer } from 'clownface'
import $rdf from 'rdf-ext'
import { md } from '@cube-creator/core/namespace'
import * as ns from '@cube-creator/core/namespace'

Check warning on line 9 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L9

Added line #L9 was not covered by tests
import conditional from 'express-conditional-middleware'
import { isMultipart } from '@cube-creator/express/multipart'
import { shaclValidate } from '../middleware/shacl'
Expand All @@ -29,19 +29,26 @@
const offset = (page - 1) * pageSize
const queryParams = {
freetextQuery: query.has(hydra.freetextQuery).out(hydra.freetextQuery).value,
validThrough: query.has(md.onlyValidTerms, query.literal(true)).terms.length ? new Date() : undefined,
validThrough: query.has(ns.md.onlyValidTerms, query.literal(true)).terms.length ? new Date() : undefined,

Check warning on line 32 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L32

Added line #L32 was not covered by tests
limit: pageSize,
offset,
includeDeprecated: $rdf.literal(query.has(owl.deprecated).out(owl.deprecated).value || 'false', xsd.boolean),

Check warning on line 35 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L35

Added line #L35 was not covered by tests
}

const collection = getCollection({
view: $rdf.namedNode(req.absoluteUrl()),
memberQuads: await getSharedDimensions(streamClient, queryParams),
collectionType: md.SharedDimensions,
data: await getSharedDimensions(streamClient, queryParams),
collectionType: ns.md.SharedDimensions,

Check warning on line 41 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L40-L41

Added lines #L40 - L41 were not covered by tests
memberType: schema.DefinedTermSet,
collection: req.hydra.resource.term,
})

collection.addOut(ns.query.templateMappings, templateMappings => {
for (const { predicate, object } of query.dataset) {
templateMappings.addOut(predicate, object)
}
})

Check warning on line 51 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L46-L51

Added lines #L46 - L51 were not covered by tests
return res.dataset(collection.dataset)
})

Expand Down Expand Up @@ -83,15 +90,15 @@
const queryParams = {
sharedDimensions: sharedDimensions.map(rewriteTerm),
freetextQuery: query.has(hydra.freetextQuery).out(hydra.freetextQuery).value,
validThrough: query.has(md.onlyValidTerms, query.literal(true)).terms.length ? new Date() : undefined,
validThrough: query.has(ns.md.onlyValidTerms, query.literal(true)).terms.length ? new Date() : undefined,

Check warning on line 93 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L93

Added line #L93 was not covered by tests
limit: pageSize,
offset,
}

const collection = getCollection({
memberQuads: await getSharedTerms(queryParams, parsingClient),
data: await getSharedTerms(queryParams, parsingClient),

Check warning on line 99 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L99

Added line #L99 was not covered by tests
memberType: schema.DefinedTerm,
collectionType: md.SharedDimensionTerms,
collectionType: ns.md.SharedDimensionTerms,

Check warning on line 101 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L101

Added line #L101 was not covered by tests
collection: termsCollectionId(sharedDimensions, queryParams.freetextQuery),
})

Expand Down Expand Up @@ -119,9 +126,9 @@
export const post = conditional(isMultipart, postImportedDimension, postDirect)

export const injectTermsLink: Enrichment = async (req, pointer) => {
pointer.deleteOut(md.terms).addOut(md.terms, termsCollectionId([pointer.term]))
pointer.deleteOut(ns.md.terms).addOut(ns.md.terms, termsCollectionId([pointer.term]))

Check warning on line 129 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L129

Added line #L129 was not covered by tests
}

export const injectExportLink: Enrichment = async (req, pointer) => {
pointer.deleteOut(md.export).addOut(md.export, pointer.namedNode(`${env.MANAGED_DIMENSIONS_BASE}dimension/_export?dimension=${pointer.value}`))
pointer.deleteOut(ns.md.export).addOut(ns.md.export, pointer.namedNode(`${env.MANAGED_DIMENSIONS_BASE}dimension/_export?dimension=${pointer.value}`))

Check warning on line 133 in apis/shared-dimensions/lib/handlers/shared-dimensions.ts

View check run for this annotation

Codecov / codecov/patch

apis/shared-dimensions/lib/handlers/shared-dimensions.ts#L133

Added line #L133 was not covered by tests
}
1 change: 1 addition & 0 deletions apis/shared-dimensions/lib/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import env from './env'

type Shapes = 'shape/shared-dimension-create'
| 'shape/shared-dimension-update'
| 'shape/shared-dimension-search'
| 'shape/shared-dimension-term-create'
| 'shape/shared-dimension-term-update'
| 'shape/hierarchy-create'
Expand Down
Loading
Loading