Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
45 changes: 27 additions & 18 deletions docs/src/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,37 @@ npm install @tomic/lib
```

```ts
// Import the Store
import { Store, Agent, urls } from "@tomic/lib";
import { Store, Agent, core } from "@tomic/lib";

const opts = {
// --------- Create a Store ---------.
const store = new Store({
// You can create a secret from the `User settings` page using the AtomicServer UI
agent: Agent.fromSecret("my-secret-key"),
// Set a default server URL
serverUrl: "https://atomicdata.dev",
}
const store = new Store(opts);

// Get a resource
const gotResource = store.getResourceLoading(subject);
const atomString = gotResource!
.get(urls.properties.description)!
.toString();

// Create & save a new resource
const subject = 'https://atomicdata.dev/test';
const newResource = new Resource(subject);
await newResource.set(urls.properties.description, 'Hi world');
newResource.save(store);
serverUrl: "https://my-atomic-server.dev",
});

// --------- Get a resource ---------
const gotResource = await store.getResourceAsync(subject);

const atomString = gotResource.get(core.properties.description)

// --------- Create & save a new resource ---------
const newResource = await store.newResource({
subject: 'https://my-atomic-server.dev/test',
propVals: {
[core.properties.description]: 'Hi World :)'
}
});

await newResource.save(store);

// --------- Subscribe to changes (using websockets) ---------
const unsub = store.subscribe('https://my-atomic-server.dev/test', (resource) => {
// This callback is called each time a change is made to the resource client or serverside.

// Do something with the changed resource...
})
```

## Advanced usage
Expand Down