Skip to content

Commit

Permalink
#798 Streamline Resource Creation API
Browse files Browse the repository at this point in the history
  • Loading branch information
Polleps committed Jan 15, 2024
1 parent 6337c2f commit 3a507da
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
4 changes: 4 additions & 0 deletions browser/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ This changelog covers all three packages, as they are (for now) updated as a who
- [#758](https://github.com/atomicdata-dev/atomic-server/issues/758) Fix Relation column forms to close when clicking on the searchbox
- Fix server not rebuilding client when files changed.

### @tomic/lib

- [#798](https://github.com/atomicdata-dev/atomic-server/issues/798) Add `store.newResource()` to make creating new resources more easy.

## v0.36.2

### Atomic Browser
Expand Down
26 changes: 25 additions & 1 deletion browser/lib/src/store.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { jest } from '@jest/globals';
import { Resource, urls, Store } from './index.js';
import { Resource, urls, Store, core, Core } from './index.js';

describe('Store', () => {
it('renders the populate value', async () => {
Expand Down Expand Up @@ -56,4 +56,28 @@ describe('Store', () => {

expect(customFetch.mock.calls).to.have.length(1);
});

it('creates new resources using store.newResource()', async () => {
const store = new Store({ serverUrl: 'https://myserver.dev' });

const resource1 = await store.newResource<Core.Property>({
subject: 'https://myserver.dev/testthing',
parent: 'https://myserver.dev/properties',
isA: core.classes.property,
propVals: {
[core.properties.datatype]: urls.datatypes.slug,
[core.properties.shortname]: 'testthing',
},
});

expect(resource1.props.parent).to.equal('https://myserver.dev/properties');
expect(resource1.props.datatype).to.equal(urls.datatypes.slug);
expect(resource1.props.shortname).to.equal('testthing');
expect(resource1.hasClasses(core.classes.property)).to.equal(true);

const resource2 = await store.newResource();

expect(resource2.props.parent).to.equal(store.getServerUrl());
expect(resource2.get(core.properties.isA)).to.equal(undefined);
});
});
45 changes: 45 additions & 0 deletions browser/lib/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
core,
commits,
collections,
JSONValue,
} from './index.js';
import { authenticate, fetchWebSocket, startWebsocket } from './websockets.js';

Expand All @@ -38,6 +39,17 @@ type AddResourcesOpts = {
skipCommitCompare?: boolean;
};

type CreateResourceOptions = {
/** Optional subject of the new resource, if not given the store will generate a random subject */
subject?: string;
/** Parent the subject belongs to, defaults to the serverUrl */
parent?: string;
/** Subject(s) of the resources class */
isA?: string | string[];
/** Any additional properties the resource should have */
propVals?: Record<string, JSONValue>;
};

export interface StoreOpts {
/** The default store URL, where to send commits and where to create new instances */
serverUrl?: string;
Expand Down Expand Up @@ -184,6 +196,39 @@ export class Store {
this.notify(resource.__internalObject);
}

/**
* A helper function for creating new resources.
* Options take:
* subject (optional) - defaults to random subject,
* parent (optional) - defaults to serverUrl,
* isA (optional),
* properties (optional) - any additional properties to be set on the resource.
*/
public async newResource<C extends OptionalClass = UnknownClass>(
options: CreateResourceOptions = {},
): Promise<Resource<C>> {
const { subject, parent, isA, propVals } = options;

const normalizedIsA = Array.isArray(isA) ? isA : [isA];
const newSubject = subject ?? this.createSubject(normalizedIsA[0], parent);

const resource = this.getResourceLoading(newSubject, { newResource: true });

if (normalizedIsA[0]) {
await resource.addClasses(this, ...(normalizedIsA as string[]));
}

await resource.set(core.properties.parent, parent ?? this.serverUrl, this);

if (propVals) {
for (const [key, value] of Object.entries(propVals)) {
await resource.set(key, value, this);
}
}

return resource;
}

/** Checks if a subject is free to use */
public async checkSubjectTaken(subject: string): Promise<boolean> {
const r = this.resources.get(subject);
Expand Down

0 comments on commit 3a507da

Please sign in to comment.