use lossless view for full list of entity ids

This commit is contained in:
Ladd Hoffman 2024-12-26 16:52:46 -06:00
parent 3f0b5bec4e
commit c3f896f130
7 changed files with 120 additions and 53 deletions

View File

@ -118,6 +118,36 @@ Query the list of deltas ingested by this node
curl -s http://localhost:3000/deltas | jq
```
The example creates a `new TypedCollection<User>("user")` and calls `connectRhizome` to join it with the network.
The collection is synchronized across the cluster and optionally CRUD type operations are served via HTTP.
Query the list of User IDs
```bash
curl -s http://localhost:3000/user/ids
```
Query the list of User IDs
```bash
curl -s http://localhost:3000/user/ids
```
Read a User by ID
```bash
curl -s http://localhost:3000/user/taliesin-1
```
Create a User
```bash
cat <<EOF >/tmp/user.json
{"id": "optional-id",
"properties": {
"name": "required",
"nameLong": "optional",
"email": "optional"}}
EOF
curl -s -X PUT -H 'content-type:application/json' -d @/tmp/user.json http://localhost:3000/user | jq
```
# More About Concepts
## Clocks?

View File

@ -18,6 +18,8 @@ describe('Run', () => {
});
it('can put a new user and fetch it', async () => {
// Create a new record
{
const res = await fetch(`${app.apiUrl}/user`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
@ -37,18 +39,30 @@ describe('Run', () => {
age: 263
}
});
}
// TODO: Optimistic update and remove this delay
await new Promise((resolve) => setTimeout(resolve, 100));
const res2 = await fetch(`${app.apiUrl}/user/peon-1`);
const data2 = await res2.json();
expect(data2).toMatchObject({
// Read what we wrote
{
const res = await fetch(`${app.apiUrl}/user/peon-1`);
const data = await res.json();
expect(data).toMatchObject({
id: "peon-1",
properties: {
name: "Peon",
age: 263
}
});
}
// Verify our record is also in the index
{
const res = await fetch(`${app.apiUrl}/user/ids`);
const data = await res.json();
expect(data).toMatchObject({ids: [ "peon-1"]});
}
});
});

View File

@ -24,10 +24,12 @@ describe('Run', () => {
await Promise.all(apps.map((app) => app.stop()));
});
it('can create a record on node 0 and read it on node 1', async () => {
it('can create a record on app0 and read it on app1', async () => {
debug('apps[0].apiUrl', apps[0].apiUrl);
debug('apps[1].apiUrl', apps[1].apiUrl);
// Create a new record on app0
{
const res = await fetch(`${apps[0].apiUrl}/user`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
@ -35,7 +37,7 @@ describe('Run', () => {
id: "peon-1",
properties: {
name: "Peon",
age: 263
age: 741
}
})
});
@ -44,22 +46,34 @@ describe('Run', () => {
id: "peon-1",
properties: {
name: "Peon",
age: 263
age: 741
}
});
}
// TODO remove delay
await new Promise((resolve) => setTimeout(resolve, 100));
const res2 = await fetch(`${apps[1].apiUrl}/user/peon-1`);
const data2 = await res2.json();
debug('data2', data2);
expect(data2).toMatchObject({
// Read from app1
{
const res = await fetch(`${apps[1].apiUrl}/user/peon-1`);
const data = await res.json();
debug('data', data);
expect(data).toMatchObject({
id: "peon-1",
properties: {
name: "Peon",
age: 263
age: 741
}
});
}
// Verify our record is also in the index
for (const app of apps) {
const res = await fetch(`${app.apiUrl}/user/ids`);
const data = await res.json();
expect(data).toMatchObject({ids: ["peon-1"]});
}
});
});

View File

@ -169,6 +169,7 @@ export class Collection {
}
getIds(): string[] {
return Array.from(this.entities.keys());
// return Array.from(this.entities.keys());
return Array.from(this.lossless.domainEntities.keys());
}
}

View File

@ -19,7 +19,7 @@ type User = {
(async () => {
const rhizomeNode = new RhizomeNode();
const users = new TypedCollection<User>("users");
const users = new TypedCollection<User>("user");
users.rhizomeConnect(rhizomeNode);
users.onUpdate((u: Entity) => {

View File

@ -97,7 +97,7 @@ export class HttpApi {
const html = this.mdFiles.generateIndex();
this.router.get('/html', (_req: express.Request, res: express.Response) => {
res.setHeader('content-type', 'text/html').send(html);
res.setHeader('content-type', 'text/html').send(this.mdFiles.indexHtml);
});
}

View File

@ -6,7 +6,7 @@ const debug = Debug('md-files');
const docConverter = new Converter({
completeHTMLDocument: true,
// simpleLineBreaks: true,
simpleLineBreaks: false,
tables: true,
tasklists: true
});
@ -27,6 +27,7 @@ export class MDFiles {
readme?: mdFileInfo;
dirWatcher?: FSWatcher;
readmeWatcher?: FSWatcher;
latestIndexHtml?: Html;
readFile(name: string) {
const md = readFileSync(join('./markdown', `${name}.md`)).toString();
@ -69,6 +70,13 @@ export class MDFiles {
return htmlDocFromMarkdown(md);
}
get indexHtml(): Html {
if (!this.latestIndexHtml) {
this.latestIndexHtml = this.generateIndex();
}
return this.latestIndexHtml;
}
readDir() {
// Read list of markdown files from directory and
// render each markdown file as html