Skip to content

Commit

Permalink
feat: bump solid client dependency to 1.2.0 (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
YuukanOO authored Jan 2, 2021
1 parent e5586fc commit ffd5891
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 359 deletions.
643 changes: 313 additions & 330 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@
},
"homepage": "https://github.com/solideal/storage#readme",
"devDependencies": {
"typescript": "^4.0.2",
"ts-node": "^9.0.0",
"jest": "^26.4.2",
"ts-jest": "^26.3.0",
"@types/jest": "^26.0.13",
"semantic-release": "^17.1.1",
"eslint": "^7.8.1",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@typescript-eslint/eslint-plugin": "^4.1.0"
"eslint": "^7.8.1",
"jest": "^26.4.2",
"semantic-release": "^17.3.0",
"ts-jest": "^26.3.0",
"ts-node": "^9.0.0",
"typescript": "^4.0.2"
},
"dependencies": {
"@inrupt/solid-client": "^0.3.0"
"@inrupt/solid-client": "^1.2.0"
},
"publishConfig": {
"access": "public"
Expand Down
4 changes: 3 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FetchFunc } from "./types";

/**
* Global config objects. Global objects are usually a bad thing but let's face it,
* in a common Solid client application, you will authenticate the user and update
Expand All @@ -15,7 +17,7 @@ interface Config {
/**
* Global fetch methods to use to make authenticated requests.
*/
fetch?: unknown;
fetch?: FetchFunc;
}

// Internal global config object, protected from the outside.
Expand Down
6 changes: 5 additions & 1 deletion src/ldutils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ export function createThing(url: string, ...triples: Triple[]): ThingPersisted {
export function createDataset(
url: string,
...triples: Triple[]
): SolidDataset & WithResourceInfo {
): SolidDataset &
WithResourceInfo & {
internal_resourceInfo: { linkedResources: Record<string, string[]> };
} {
return Object.assign(
triples.reduce(
(ds, triple) => ds.add(createQuad(triple[0], triple[1], triple[2])),
Expand All @@ -103,6 +106,7 @@ export function createDataset(
internal_resourceInfo: {
isRawData: false,
sourceIri: url,
linkedResources: {},
},
}
);
Expand Down
2 changes: 1 addition & 1 deletion src/repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe("the `Repository` class", () => {
.mockClear();

// eslint-disable-next-line @typescript-eslint/no-empty-function
configure({ fetch: () => {} });
configure({ fetch: <any>(() => {}) });

const repo = new Repository<Bookmark>({
source: sampleDataset.internal_resourceInfo.sourceIri,
Expand Down
10 changes: 5 additions & 5 deletions src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
ThingLocal,
} from "@inrupt/solid-client";
import { Schema, Definition } from "./schema";
import { Maybe } from "./types";
import { FetchFunc, Maybe } from "./types";
import {
fetcher,
CreateOptions,
Expand All @@ -34,7 +34,7 @@ export interface Options<TData> {
* If you use the same in every repository, you may provide with the `configure({ fetch: ... })`
* instead. This one will take precedence over the global one if defined.
*/
fetch?: unknown;
fetch?: FetchFunc;

/**
* Type of data managed by a repository. You must provide an URI representing the
Expand Down Expand Up @@ -114,7 +114,7 @@ export class Repository<TData> {
const dataset = data.reduce((ds, record) => {
const dataUrl = this.schema.getUrl(record);
const thing = this.schema.write(
dataUrl ? getThing(ds, dataUrl) : createThing(), // Should we check the thing type retrieved with getThing here?
(dataUrl && getThing(ds, dataUrl)) || createThing(), // Should we check the thing type retrieved with getThing here?
record
);
this.schema.setUrl(
Expand Down Expand Up @@ -176,7 +176,7 @@ export class Repository<TData> {
if (Array.isArray(filter)) {
return filter.reduce((result, key) => {
const thing = getThing(dataset, this.schema.getUrl(key));
if (this.schema.ofType(thing)) {
if (thing && this.schema.ofType(thing)) {
result.push(this.schema.read(thing));
}
return result;
Expand All @@ -200,7 +200,7 @@ export class Repository<TData> {
options: Omit<Options<TData>, "source">,
resolveOptions?: Partial<CreateOptions> & {
webid?: string;
fetch?: unknown;
fetch?: FetchFunc;
}
): Promise<Repository<TData>> {
const repo = new Repository({
Expand Down
66 changes: 60 additions & 6 deletions src/resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
NoWebIdDefined,
resolveOrRegisterTypeLocation,
NoIndexLocationFound,
NoProfileFound,
} from "./resolver";
import { configure } from "./config";
import { createDataset, hasExactly } from "./ldutils.test";
Expand All @@ -29,6 +30,16 @@ describe("the resolveTypeLocation function", () => {
);
});

it("throws an error if no profile exists", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
.mockResolvedValue(createDataset(urls.webid));

await expect(
resolveTypeLocation(urls.type, urls.webid)
).rejects.toThrowError(NoProfileFound);
});

it("use the global webid if defined", async () => {
const spy = jest
.spyOn(solidClient, "getSolidDataset")
Expand All @@ -37,7 +48,7 @@ describe("the resolveTypeLocation function", () => {
configure({ webid: urls.webid });

await expect(resolveTypeLocation(urls.type)).rejects.toThrowError(
new NoLocationFound(urls.type).message
new NoProfileFound().message
);
expect(spy.mock.calls[0][0]).toEqual(urls.webid);
});
Expand All @@ -50,7 +61,7 @@ describe("the resolveTypeLocation function", () => {
const fetch = jest.fn();
await expect(
resolveTypeLocation(urls.type, urls.webid, fetch)
).rejects.toThrowError(new NoLocationFound(urls.type).message);
).rejects.toThrowError(new NoProfileFound().message);

expect(spy.mock.calls[0][1]).toEqual({ fetch });
});
Expand All @@ -66,11 +77,28 @@ describe("the resolveTypeLocation function", () => {

await expect(
resolveTypeLocation(urls.type, urls.webid)
).rejects.toThrowError(new NoLocationFound(urls.type).message);
).rejects.toThrowError(new NoProfileFound().message);

expect(spy.mock.calls[0][1]).toEqual({ fetch });
});

it("throws a NoLocationFound is no location could be found", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
.mockResolvedValue(
createDataset(urls.webid, [
urls.webid,
"http://some.predicate",
"some value",
])
)
.mockClear();

await expect(
resolveTypeLocation(urls.type, urls.webid)
).rejects.toThrowError(new NoLocationFound(urls.type).message);
});

it("returns the first available location from the indexes", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
Expand Down Expand Up @@ -102,6 +130,20 @@ describe("the resolveTypeLocation function", () => {
describe("the resolveOrRegisterTypeLocation function", () => {
beforeEach(() => configure({ webid: undefined, fetch: undefined }));

it("throws an error if no profile exists", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
.mockResolvedValue(createDataset(urls.webid));

await expect(
resolveOrRegisterTypeLocation(
urls.type,
{ path: urls.newBookmarks, index: "public" },
urls.webid
)
).rejects.toThrowError(NoProfileFound);
});

it("returns the location if already defined for a type", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
Expand Down Expand Up @@ -151,13 +193,19 @@ describe("the resolveOrRegisterTypeLocation function", () => {
path: urls.newBookmarks,
index: "public",
})
).rejects.toThrow(new NoIndexLocationFound().message);
).rejects.toThrow(new NoProfileFound().message);
});

it("throw an error if no public index exists", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
.mockResolvedValue(createDataset(urls.webid))
.mockResolvedValue(
createDataset(urls.webid, [
urls.webid,
"http://some.predicate",
"some value",
])
)
.mockClear();

await expect(
Expand All @@ -172,7 +220,13 @@ describe("the resolveOrRegisterTypeLocation function", () => {
it("throw an error if no private index exists", async () => {
jest
.spyOn(solidClient, "getSolidDataset")
.mockResolvedValue(createDataset(urls.webid))
.mockResolvedValue(
createDataset(urls.webid, [
urls.webid,
"http://some.predicate",
"some value",
])
)
.mockClear();

await expect(
Expand Down
29 changes: 24 additions & 5 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
createThing,
setThing,
} from "@inrupt/solid-client";
import { Fetcher, Maybe } from "./types";
import { FetchOptions, FetchFunc, Maybe } from "./types";
import { setting } from "./config";
import { solid } from "./namespaces";
import { Schema, is } from "./schema";
Expand All @@ -25,6 +25,15 @@ export class NoWebIdDefined extends Error {
}
}

/**
* Error thrown when no profile has been found in a webid document.
*/
export class NoProfileFound extends Error {
constructor() {
super("no profile document was found");
}
}

/**
* Thrown when no location has been found for a particular type.
*/
Expand Down Expand Up @@ -67,7 +76,7 @@ const typeRegistrationSchema = new Schema<{
export async function resolveTypeLocation(
type: string,
webid?: string,
fetch?: unknown
fetch?: FetchFunc
): Promise<string> {
const options = fetcher(fetch);
const wid = webid ?? setting("webid");
Expand All @@ -77,6 +86,11 @@ export async function resolveTypeLocation(
}

const profile = getThing(await getSolidDataset(wid, options), wid);

if (!profile) {
throw new NoProfileFound();
}

const location = await findFirstAvailableTypeLocation(
type,
[
Expand Down Expand Up @@ -107,7 +121,7 @@ export async function resolveOrRegisterTypeLocation(
type: string,
options: CreateOptions,
webid?: string,
fetch?: unknown
fetch?: FetchFunc
): Promise<string> {
try {
return await resolveTypeLocation(type, webid, fetch);
Expand All @@ -120,6 +134,11 @@ export async function resolveOrRegisterTypeLocation(
const wid = (webid ?? setting("webid"))!; // This one is safe since it will be thrown by the first call
const fetchOptions = fetcher(fetch);
const profile = getThing(await getSolidDataset(wid, fetchOptions), wid);

if (!profile) {
throw new NoProfileFound();
}

const indexUrl = getUrl(
profile,
options.index === "public"
Expand Down Expand Up @@ -161,7 +180,7 @@ export async function resolveOrRegisterTypeLocation(
async function findFirstAvailableTypeLocation(
type: string,
urls: (string | null)[],
options?: Fetcher
options?: FetchOptions
): Promise<Maybe<string>> {
for (const url of urls) {
if (!url) {
Expand Down Expand Up @@ -195,7 +214,7 @@ async function findFirstAvailableTypeLocation(
* Returns a fetcher options used for getSolidDataset and saveSolidDatasetAt by
* using the global one if given fetch is undefined.
*/
export function fetcher(fetch?: unknown): Fetcher | undefined {
export function fetcher(fetch?: FetchFunc): FetchOptions | undefined {
const f = fetch ?? setting("fetch");
return f && { fetch: f };
}
13 changes: 11 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@
*/
export type Maybe<T> = T | undefined | null;

/**
* Fetch API signature as expected by solid-client.
*/
export type FetchFunc = ((
input: RequestInfo,
init?: RequestInit | undefined
) => Promise<Response>) &
typeof globalThis.fetch;

/**
* Interface to represent an object with a fetch function used to make requests
* to a user pod. It represents the interface expected by *SolidDataset* functions.
*/
export interface Fetcher {
fetch: unknown;
export interface FetchOptions {
fetch?: FetchFunc;
}

0 comments on commit ffd5891

Please sign in to comment.