Creating new resource using solid-auth-client

Been trying to create a new photo resource on public folder using solid-auth-client but so far to no avail.

The following code demonstrates the process:

  async savePhoto($event: PhotoEvent) {
    await this.prepareBaseUrl();

    this.solid.fetch(
      `${this.baseUrl}/public/photo.png`,
      {
        method: 'POST',
        body: $event.src.split(',')[1],
        headers: {'Content-Type': $event.mime},
        })
      .then(console.log);
  }

  private async prepareBaseUrl() {
    if (this.baseUrl) return;

    const session = await this.solid.currentSession();
    const webIdUrl = new URL(session.webId);
    this.baseUrl = `${webIdUrl.protocol}//${webIdUrl.hostname}`;
  }

And the error:

  1. Request URL:

https://.inrupt.net/public/photo.png

  1. Request Method:

POST

  1. Status Code:

404 Not Found

  1. Remote Address:

35.168.136.65:443

Any idea what I’m doing wrong?

You might want to use Solid-File-Client which handles reading and write of resources including non-RDF resources like photographs with higher level methods such as fc.createFile(URI,content,content-tyep) which handles the details of PUT and POST behind the scenes. If you are going to do this manually with solid-auth-client then you need to learn more about how POST works. A POST should send the URI of the parent container, not of the image file. The name of the image file belongs in the slug field of the POST, not the URI field.

2 Likes

Here is what a POST looks like:

solid.auth.fetch(  parentContainerURI, {
  method:"POST",
  headers:{
    slug: "foo.png"
    link: '<http://www.w3.org/ns/ldp#Resource>; rel="type"',
    "content-type": "image/png"
  },
  body: contentOfTheFile
})

This URL appears to be missing something before .inrupt.net. Is the error message you posted correct, and if so, could you check the values of this.baseUrl and session.webId?

That was on purpose so I would not give my webid

thanks jeffz, will try this

I was able to make it work after your instructions:

  async savePhoto($event: PhotoEvent) {
    console.log(`Saving Photo: ${$event.src}`);
    await this.prepareBaseUrl();

    await this.solid.fetch(
      `${this.baseUrl}/private`,
      {
        method: 'POST',
        body: await this.b64toBlob($event.src),
        headers: {
          slug: 'photo.png',
          link: '<http://www.w3.org/ns/ldp#Resource>; rel="type"',
          'Content-Type': $event.mime,
        },
      });
  }

  private async prepareBaseUrl() {
    if (this.baseUrl) {
      return;
    }
    const session = await this.solid.currentSession();
    const webIdUrl = new URL(session.webId);
    this.baseUrl = `${webIdUrl.protocol}//${webIdUrl.hostname}`;
  }

  private async b64toBlob(imgsrc): Promise<Blob> {
    return await fetch(imgsrc).then(res => res.blob());
  }

I’ve also tried to use solid-file-client lib but to no avail.
There was no way to make it work. Kept complaining about not having a constructor.

import { SolidFileClient } from 'solid-file-client';

 private async prepareBaseUrl() {
    if (this.baseUrl && this.fc) {
      return;
    }

    this.fc = new SolidFileClient(this.solidAuthService); // Error Here saying that no constructor is available 
                                                          // (tried different ways of importing it but to no avail)

    const session = await this.solid.currentSession();
    const webIdUrl = new URL(session.webId);
    this.baseUrl = `${webIdUrl.protocol}//${webIdUrl.hostname}`;
  }

That’s not how to create a solid-file-client object.

In a nodejs script using ES6 :

   import auth from 'solid-auth-cli'
   import FileClientObejct from 'solid-file-client'
   const myFileClient = new FileClientObject( auth )
   myFileClient.readFile( someURI ).then( response=>{ ... } )

In a browser script:

  <script src="solid-auth-client.bundle.js"></script>
  <script src="solid-file-client.bundle.js"></script>
  <script>
    const myFileClient = new SolidFileClient(solid.auth)
    myFileClient.readFile( someURI ).then( response=>{ ... } )

I’m using Typescript on an Angular app.
What I had was pretty much the same. Looks different because of the “auth” service, but I’ll give it another try since it will make things much easier to develop and maintain.

1 Like

Got solid-file-client working as well:

...
import { SolidAuthClient } from 'solid-auth-client';
import SolidFileClient from 'solid-file-client';

@Injectable({
  providedIn: 'root'
})
export class SolidResourcesService {

  solidAuth: SolidAuthClient;
  baseUrl: string = undefined;
  solidFileClient: SolidFileClient;

  constructor(
    private solidAuthService: SolidAuthService,
  ) {
    this.solidAuth = this.solidAuthService.solidAuth();
    this.solidFileClient = new SolidFileClient(this.solidAuth);
  }

  async savePhoto($event: PhotoEvent) {
    console.log(`Saving Photo: ${$event.src}`);
    await this.prepareBaseUrl();

    this.solidFileClient.postFile(`${this.baseUrl}/private/photoFile.png`, await this.b64toBlob($event.src), $event.mime);
  }

  private async prepareBaseUrl() {
    if (this.baseUrl) {
      return;
    }

    const session = await this.solidAuth.currentSession();
    const webIdUrl = new URL(session.webId);
    this.baseUrl = `${webIdUrl.protocol}//${webIdUrl.hostname}`;
  }

  private async b64toBlob(imgsrc): Promise<Blob> {
    return await fetch(imgsrc).then(res => res.blob());
  }
}

I don’t think that there’s a type definition for solid-file-client on DefinitelyTyped.
It would be nice to have the type defined for easier Typescript integration.
Installation would be as easy as:

npm install @Types/solid-file-client 

Something for the thought. After looking at the solid-file-client code, the API seems to be quite simple and straight forward, so, it shouldn’t take long to add type support for Typescript.

Cheers and thanks for the help.

2 Likes