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

feat: search projects #1330

Merged
merged 13 commits into from
Nov 21, 2022
Merged

feat: search projects #1330

merged 13 commits into from
Nov 21, 2022

Conversation

tpluscode
Copy link
Collaborator

@tpluscode tpluscode commented Nov 15, 2022

search.mp4

@changeset-bot
Copy link

changeset-bot bot commented Nov 15, 2022

🦋 Changeset detected

Latest commit: bbdce1d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@cube-creator/core-api Minor
@cube-creator/ui Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov-commenter
Copy link

codecov-commenter commented Nov 15, 2022

Codecov Report

Merging #1330 (51c4b91) into master (b715403) will decrease coverage by 0.38%.
The diff coverage is 9.85%.

@@            Coverage Diff             @@
##           master    #1330      +/-   ##
==========================================
- Coverage   81.41%   81.03%   -0.39%     
==========================================
  Files         194      195       +1     
  Lines       13453    13509      +56     
  Branches      753      754       +1     
==========================================
- Hits        10953    10947       -6     
- Misses       2492     2554      +62     
  Partials        8        8              
Impacted Files Coverage Δ
apis/core/lib/auth.ts 0.00% <0.00%> (ø)
apis/core/lib/domain/cube-projects/search.ts 0.00% <0.00%> (ø)
apis/core/lib/domain/cube-projects/create.ts 84.11% <100.00%> (-0.37%) ⬇️
apis/core/lib/domain/cube-projects/import.ts 98.19% <100.00%> (-0.03%) ⬇️
packages/core/namespaces/shapes.ts 96.55% <100.00%> (+0.12%) ⬆️
packages/model/Project.ts 100.00% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

Base automatically changed from project-meta to master November 16, 2022 10:37
@tpluscode tpluscode marked this pull request as ready for review November 16, 2022 11:54
Comment on lines +16 to +26
<cube-projects/status> {
<cube-projects/status> a ${hydra.Collection}, ${schema.DefinedTermSet} ;
${hydra.member} ${Draft}, ${Published} ;
.

${Published} a ${schema.DefinedTerm} ;
${rdfs.label} "Published" .

${Draft} a ${schema.DefinedTerm} ;
${rdfs.label} "Draft" .
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A static collection of statuses to populate the search dropdown

Comment on lines +189 to +216
${shape('cube-project/search')} {
${shape('cube-project/search')} a ${sh.NodeShape}, ${hydra.Resource} ;
${sh.property} [
${sh.name} "Keyword Search" ;
${sh.order} 10 ;
${sh.path} ${hydra.freetextQuery} ;
${sh.minCount} 1 ;
${sh.maxCount} 1 ;
],
[
${sh.name} "Author" ;
${sh.path} ${dcterms.creator} ;
${sh.order} 20 ;
${sh.minCount} 1 ;
${sh.maxCount} 1 ;
${dash.editor} ${dash.InstancesSelectEditor} ;
${hydra.collection} </users> ;
],
[
${sh.name} "Status" ;
${sh.path} ${schema.creativeWorkStatus} ;
${sh.order} 30 ;
${sh.minCount} 1 ;
${sh.maxCount} 1 ;
${dash.editor} ${dash.InstancesSelectEditor} ;
${hydra.collection} </cube-projects/status> ;
] ;
.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shape which defines the fields for search form

Comment on lines +5 to +12
<users> {
<users> a ${hydra.Collection} ;
${hydra.manages} [
${hydra.property} ${rdf.type} ;
${hydra.object} ${schema.Person}
] .
}
`
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reflects a bigger change where users actually become independent resources in their own named graphs. Here, the collection is added to retrieve them

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this change!

Comment on lines 94 to 123
function updateUserResource(): RequestHandler {
const users = new TermSet()

return (req, res, next) => {
if (req.user?.id && !users.has(req.user.id)) {
const { id, name, sub } = req.user
users.add(id)

req.once('end', () => {
DELETE`
GRAPH ${id} {
${id} ${schema.name} ?name .
}
`.INSERT`
GRAPH ${id} {
${id} ${schema.name} "${name || sub}"; a ${schema.Person} .
}
`.WHERE`
OPTIONAL {
GRAPH ${id} {
${id} ${schema.name} ?name .
}
}
`.execute(req.labyrinth.sparql.query)
.catch(warning)
})
}
next()
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we want users as resources, they need to be created at some point.

I added this middleware to run after authentication, so that any one that successfully logs in will have their user graph created automatically.

Importantly, the users set ensures that as long the API process is running, every user will be created only once.

A future improvement could be to actually load existing users in the beginning. On the other hand, this way will also keep the names updated, shall they change on the auth provider...

@@ -103,7 +101,7 @@ export async function importProject({
const projectNode = await store.createMember(projectsCollection.term, id.cubeProject(label))

const project = createMinimalProject(projectNode, {
creator: { id: user, name: userName },
creator: user,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we now have graphs for users, only the URI will be stored with the project now, unlike the lazy duplication we've had so far

Comment on lines +6 to +28
export function byFreeText({ subject, object }: Pattern) {
return sparql`
${subject} ${rdfs.label}|${schema.alternateName}|${schema.disambiguatingDescription} ?freetextSearched .

FILTER(REGEX(?freetextSearched, "${object.value}", "i"))
`
}

export function byStatus({ subject, predicate, object }: Pattern) {
return sparql`
${subject} ${cc.dataset} ?dataset .

graph ?dataset {
?dataset ${predicate} ${object.term} .
}
`
}

export function byUser({ subject, predicate, object }: Pattern) {
return sparql`
${subject} ${predicate} ${object.term} .
`
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions which provide the SPARQL patterns to search by respective query params (see apis/core/hydra/projects.ttl)

+ }))
})

req.dataset = () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A temporary patch to hydra-box which fixes handling of + in query string values. See zazuko/kopflos#104

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide me a link to learn more about these kind of patches? I haven't seen this before

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done using patch-package

When you modify a library in node_modules/foo-bar, you the run npx patch-package foo-bar. That will create the patch file.

Running patch-package in postinstall script applies the package when installed


export const displayLanguage = ['en', 'de', 'fr', '']

export function serializeProjectsCollection (collection: ProjectsCollection): ProjectsCollection {
return Object.freeze({
...serializeResource(collection),
searchParams: clone(collection.pointer),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server echoes the query string params as RDF. This makes a copy so that the form does modify the representation retrieved from the API

Comment on lines 114 to 125
onSearch (e: CustomEvent) {
const template = this.operation?.get<IriTemplate>(hydraBox.variables)
if (template && e.detail?.value) {
const url = template.expand(e.detail.value)
const search = new URLSearchParams(url.substring(url.indexOf('?')))
const entries = [...search.entries()].filter(([, value]) => !!value && value !== '""')

this.$router.push({
query: Object.fromEntries(entries),
})
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't exactly like this.

The search is triggered by router query param change. In a different scenario I would construct the URL from template and then use that as a whole to change the "route"

My best solution here is to round-trip the expanded template back into the query params and push them to the router

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it's difficult to understand. Perhaps you could factorize it in a function with a name ('pushSearchRoute'?) to explain the intent?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All right, I moved that logic to a function exported from @/router.

A bit of a mouthful. That might be the longest function name in this project now 🙈

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, they don't charge for number of chars in function names ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Else Java people would be richest :D

@tpluscode
Copy link
Collaborator Author

tpluscode commented Nov 16, 2022

@l00mi a bigger change to be aware of is that users become their own resources. Until now the user names were simply stored with projects themselves

graph <project> {
  <project> dcterms:creator <user> .
  <user> schema:name "John Doe" .
}

Now it will be like

graph <project> {
  <project> dcterms:creator <user> .
}

graph <user> {
  <user> schema:name "John Doe" ; a schema:Person , hydra:Resource .
}

Projects already referred to users with URIs so I don't find this a breaking change per se.
User resources will be automatically created as users sign in. For existing users, I would run an update like this on every environment:

prefix dcterms: <http://purl.org/dc/terms/>
prefix schema: <http://schema.org/>
prefix cc: <https://cube-creator.zazuko.com/vocab#>
prefix hydra: <http://www.w3.org/ns/hydra/core#>

DELETE {
  GRAPH ?project {
    ?user schema:name ?name
  }
}
INSERT {
  GRAPH ?user {
    ?user schema:name ?name ; a schema:Person , hydra:Resource .
  }
} WHERE {
  GRAPH ?project {
    ?project a cc:CubeProject ; dcterms:creator ?user .
    ?user schema:name ?name .
  }
}

req.once('end', () => {
DELETE`
GRAPH ${id} {
${id} ${schema.name} ?name .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If users live in their own named graph, wouldn't it be cleaner to delete the graph as a whole?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this is but not deleting the whole graph. Only schema:name :)

@@ -17,6 +17,11 @@
Cancel
</o-button>
</div>
<div class="control" v-if="showClear">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI-wise, I think it would look better with an X instead of a button 'clear', (optional)

image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I expected you'd say that. Unfortunately, oruga select does not appear to allow that

Copy link

@cristianvasquez cristianvasquez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me!

Only (optional) comments

@tpluscode tpluscode merged commit 913570f into master Nov 21, 2022
@tpluscode tpluscode deleted the project-search branch November 21, 2022 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants