Question about retrieving data

Hi @wenjingli-qa the first issue is totaly normal as solid.community does not existe anymore, the new server is solidcommunity.net, so this should work https://solid.github.io/ldflex-playground/#[‘https%3A%2F%2Fspoggy-test9.solidcommunity.net%2Fprofile%2Fcard%23me’].vcard%24fn instead of

as @Vincent said

but for many reason I personnally feel more easy with the first stack (folder files & subfolders with solid-file-client, one line access with ldflex…) whereas i know that the second should certainely replace the first

then for the ldflex/webpack issue, @RubenVerborgh gave me a solution here that could help you how to externalize query-ldflex in shighl webpack.config.js ? · Issue #57 · LDflex/Query-Solid · GitHub but Make Webpack configuration for an ES6 module · Issue #25 · LDflex/Query-Solid · GitHub

that work but for other projects, the solution i first found is importing solid-auth-client & ldflex in the index.html as an “old” js ( lines 9 to 11)

and then get it in my modules (line 1 and line 16) with

At some point there was an issue with communica lib that is used by ldflex so i swithched to rdflib Help using different libs: rdflibjs, tripledoc, ldflex, solid-file-client - #14

Another way I recently used is (line 11 & 48)

Does it solve your issue ?

1 Like

thank you very much!!! it’s worked to import solid-auth-client & ldflex in the index.html, i have got the email from profile, :smiley: :cherry_blossom: :cherry_blossom:

2 Likes

Nice :slightly_smiling_face::+1: please don’t spam my mail :wink:

1 Like

I’m playing around and I made a small React application, when I login it takes my profile card:

https://pod.inrupt.com/dieter/profile/card

I’m able to get my first name, role, etc… but not my home & work email address.
Can someone give me a suggestion how to do that?

This is how my application looks like:

import React, { useEffect, useState } from 'react'
import {
  LoginButton,
  LogoutButton,
  CombinedDataProvider,
  Text,
  Image
} from '@inrupt/solid-ui-react'
import { FOAF, VCARD } from '@inrupt/vocab-common-rdf'
import { handleIncomingRedirect } from '@inrupt/solid-client-authn-browser'

const App = () => {
  const [idp] = useState('https://broker.pod.inrupt.com')
  const [currentUrl] = useState("http://localhost:8080")
  const [info, setInfo] = useState(false)

  const logout = () => {
    setInfo(false)
  }

  useEffect(() => {
    handleIncomingRedirect({
      restorePreviousSession: true
    }).then(async info => {
      setInfo(info)
      console.log(`Logged in with WebID [${info.webId}]`)
    })
  }, [])

  return (
    <div>
      <LoginButton
        authOptions={{ clientName: 'test' }}
        oidcIssuer={idp}
        redirectUrl={currentUrl}
        onError={console.error}
      ></LoginButton>
      <LogoutButton
        onLogout={logout}
      ></LogoutButton>
      {info.isLoggedIn ? 'yes' : 'no'}<br />
      <CombinedDataProvider datasetUrl={info.webId} thingUrl={info.webId}>
        FOAF.name: <Text property={FOAF.name} /><br />
        VCARD.fn: <Text property={VCARD.fn} /><br />
        VCARD.role: <Text property={VCARD.role} /><br />
        VCARD.organization_name: <Text property={VCARD.organization_name} /><br />
        VCARD.photo: <Image property={VCARD.photo} width={480} />
      </CombinedDataProvider>
    </div>
  )
}

export default App

That’s because those addresses are listed in separate Things (i.e. https://pod.inrupt.com/dieter/profile/card#164552511412522672518959692933 and https://pod.inrupt.com/dieter/profile/card#16455414726144387054963402375, rather than https://pod.inrupt.com/dieter/profile/card#me.

I’m not familiar with the React components and <CombinedDataProvider>, but with plain solid-client, you’d obtain them something like this:

const dataset = await getSolidDataset("https://pod.inrupt.com/dieter/profile/card");

const profileThing = getThing(dataset, "https://pod.inrupt.com/dieter/profile/card#me");

const addressThingUrls = getUrlAll(profileThing, "http://www.w3.org/2006/vcard/ns#hasEmail");
const addressThings = addressUrls.map(addressThingUrl => getThing(dataset, addressThingUrl));

const workAddressThing = addressThings.find(addressThing => getUrl(addressThing, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") === "http://www.w3.org/2006/vcard/ns#Work");
const workAddress = getUrl(workAddressThing, "http://www.w3.org/2006/vcard/ns#value");

const homeAddressThing = addressThings.find(addressThing => getUrl(addressThing, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") === "http://www.w3.org/2006/vcard/ns#Home");
const homeAddress = getUrl(homeAddressThing, "http://www.w3.org/2006/vcard/ns#value");

Possibly, this structure makes more sense if you use a Thing-based viewer like Penny: https://penny.vincenttunru.com/explore/?url=https%3A%2F%2Fpod.inrupt.com%2Fdieter%2Fprofile%2Fcard

1 Like

Thanks for pushing me in the right direction. I was able to make it work with plain solid-client and solid-ui-react. Also @inrupt/vocab-common-rdf is a very useful package!

Maybe it is helpful for someone else:

App.js component

import React, { useEffect, useState } from 'react'
import {
  LoginButton,
  LogoutButton,
  CombinedDataProvider,
  Text,
  Image
} from '@inrupt/solid-ui-react'
import {
  getSolidDataset,
  getThing,
  getUrl,
  getUrlAll,
  getStringNoLocale
} from '@inrupt/solid-client'
import { FOAF, VCARD, RDF } from '@inrupt/vocab-common-rdf'
import { handleIncomingRedirect, fetch } from '@inrupt/solid-client-authn-browser'
import _ from 'lodash'
import Emails from './Emails'

const App = () => {
  const [idp] = useState('https://broker.pod.inrupt.com')
  const [currentUrl] = useState("http://localhost:8080")
  const [auth, setAuth] = useState(false)
  const [profile, setProfile] = useState(false)
  const [dataset, setDataset] = useState(false)

  const logout = () => {
    setAuth(false)
  }

  useEffect(() => {
    handleIncomingRedirect({
      restorePreviousSession: true
    }).then(async info => {
      setAuth(info)
      console.log(`Logged in with WebID [${info.webId}]`)
    })
  }, [])

  useEffect(async () => {
    if (!auth.webId) {
      return
    }

    setDataset(
      await getSolidDataset(
        auth.webId,
        { fetch: fetch }
      )
    )
  }, [auth.webId])

  useEffect(() => {
    if (!auth.webId || !dataset) {
      setProfile(false)
      return
    }

    setProfile(
      getThing(dataset, auth.webId)
    )
  }, [auth.webId, dataset])

  const renderEmails = () => {
    if (!profile) {
      return
    }

    const emailAddressUrls = getUrlAll(profile, VCARD.hasEmail)
    const emailAddressThings = emailAddressUrls.map(url => getThing(dataset, url))

    const workEmailAddressThing = emailAddressThings.find(thing => getUrl(thing, RDF.type) === VCARD.Work)
    const workEmailAddress = getUrl(workEmailAddressThing, VCARD.value)

    const homeEmailAddressThing = emailAddressThings.find(thing => getUrl(thing, RDF.type) === VCARD.Home)
    const homeEmailAddress = getUrl(homeEmailAddressThing, VCARD.value)

    return (
      <>
        Work: {workEmailAddress}<br />
        Home: {homeEmailAddress}<br />
      </>
    )
  }

  const renderInfo = () => profile &&
    <div>
      <strong>Using solid-client</strong><br />
      FOAF.name: {getStringNoLocale(profile, FOAF.name)}<br />
      VCARD.fn: {getStringNoLocale(profile, VCARD.fn)}<br />
      VCARD.role: {getStringNoLocale(profile, VCARD.role)}<br />
      VCARD.organization_name: {getStringNoLocale(profile, VCARD.organization_name)}<br />
      VCARD.hasPhoto:<br />
      <img src={getUrl(profile, VCARD.hasPhoto)}/><br />
      {renderEmails()}
    </div>

  return (
    <div>
      <LoginButton
        authOptions={{ clientName: 'test' }}
        oidcIssuer={idp}
        redirectUrl={currentUrl}
        onError={console.error}
      ></LoginButton>
      <LogoutButton
        onLogout={logout}
      ></LogoutButton>
      {auth.isLoggedIn ? 'yes' : 'no'}<br />

      {renderInfo()}

      <strong>Using solid-ui-react</strong><br />
      <CombinedDataProvider
        datasetUrl={auth.webId}
        thingUrl={auth.webId}
      >
        FOAF.name: <Text property={FOAF.name} /><br />
        VCARD.fn: <Text property={VCARD.fn} /><br />
        VCARD.role: <Text property={VCARD.role} /><br />
        VCARD.organization_name: <Text property={VCARD.organization_name} /><br />
        VCARD.photo:<br /><Image property={VCARD.hasPhoto} width={480} /><br />
        <Emails></Emails>
      </CombinedDataProvider>
    </div>
  )
}

export default App

emails.js component

import { getThing, getUrl, getUrlAll } from '@inrupt/solid-client'
import { DatasetContext, useThing, CombinedDataProvider, Value } from '@inrupt/solid-ui-react'
import React, { useContext } from 'react'
import { RDF, VCARD } from '@inrupt/vocab-common-rdf'

const emails = () => {
  const { solidDataset } = useContext(DatasetContext)
  const { thing } = useThing()
  const contactDetailUrls = getUrlAll(thing, VCARD.hasEmail)
  const contactDetailThings = contactDetailUrls.map((url) => ({
    solidDataset,
    thing: getThing(solidDataset, url)
  }));

  const getLabel = (thing, type) => {
    switch(getUrl(thing, type)) {
      case VCARD.Work:
        return 'Work'
      case VCARD.Home:
        return 'Home'
      default:
        return
    }
  }

  return contactDetailThings.map((contactDetailThing, index) =>
    <React.Fragment key={index}>
      <CombinedDataProvider
        solidDataset={contactDetailThing.solidDataset}
        thing={contactDetailThing.thing}
      >
        {getLabel(contactDetailThing.thing, RDF.type)}
        <Value dataType="url" property={VCARD.value} />
      </CombinedDataProvider><br/>
    </React.Fragment>
  )
}

export default emails
1 Like