use lossless view for full list of entity ids
This commit is contained in:
parent
3f0b5bec4e
commit
c3f896f130
30
README.md
30
README.md
|
@ -118,6 +118,36 @@ Query the list of deltas ingested by this node
|
||||||
curl -s http://localhost:3000/deltas | jq
|
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
|
# More About Concepts
|
||||||
|
|
||||||
## Clocks?
|
## Clocks?
|
||||||
|
|
|
@ -18,37 +18,51 @@ describe('Run', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can put a new user and fetch it', async () => {
|
it('can put a new user and fetch it', async () => {
|
||||||
const res = await fetch(`${app.apiUrl}/user`, {
|
// Create a new record
|
||||||
method: 'PUT',
|
{
|
||||||
headers: {'Content-Type': 'application/json'},
|
const res = await fetch(`${app.apiUrl}/user`, {
|
||||||
body: JSON.stringify({
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: "peon-1",
|
||||||
|
properties: {
|
||||||
|
name: "Peon",
|
||||||
|
age: 263
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
expect(data).toMatchObject({
|
||||||
id: "peon-1",
|
id: "peon-1",
|
||||||
properties: {
|
properties: {
|
||||||
name: "Peon",
|
name: "Peon",
|
||||||
age: 263
|
age: 263
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
}
|
||||||
const data = await res.json();
|
|
||||||
expect(data).toMatchObject({
|
|
||||||
id: "peon-1",
|
|
||||||
properties: {
|
|
||||||
name: "Peon",
|
|
||||||
age: 263
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// TODO: Optimistic update and remove this delay
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
const res2 = await fetch(`${app.apiUrl}/user/peon-1`);
|
// Read what we wrote
|
||||||
const data2 = await res2.json();
|
{
|
||||||
expect(data2).toMatchObject({
|
const res = await fetch(`${app.apiUrl}/user/peon-1`);
|
||||||
id: "peon-1",
|
const data = await res.json();
|
||||||
properties: {
|
expect(data).toMatchObject({
|
||||||
name: "Peon",
|
id: "peon-1",
|
||||||
age: 263
|
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"]});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,42 +24,56 @@ describe('Run', () => {
|
||||||
await Promise.all(apps.map((app) => app.stop()));
|
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[0].apiUrl', apps[0].apiUrl);
|
||||||
debug('apps[1].apiUrl', apps[1].apiUrl);
|
debug('apps[1].apiUrl', apps[1].apiUrl);
|
||||||
|
|
||||||
const res = await fetch(`${apps[0].apiUrl}/user`, {
|
// Create a new record on app0
|
||||||
method: 'PUT',
|
{
|
||||||
headers: {'Content-Type': 'application/json'},
|
const res = await fetch(`${apps[0].apiUrl}/user`, {
|
||||||
body: JSON.stringify({
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: "peon-1",
|
||||||
|
properties: {
|
||||||
|
name: "Peon",
|
||||||
|
age: 741
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
expect(data).toMatchObject({
|
||||||
id: "peon-1",
|
id: "peon-1",
|
||||||
properties: {
|
properties: {
|
||||||
name: "Peon",
|
name: "Peon",
|
||||||
age: 263
|
age: 741
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
}
|
||||||
const data = await res.json();
|
|
||||||
expect(data).toMatchObject({
|
|
||||||
id: "peon-1",
|
|
||||||
properties: {
|
|
||||||
name: "Peon",
|
|
||||||
age: 263
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// TODO remove delay
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
const res2 = await fetch(`${apps[1].apiUrl}/user/peon-1`);
|
// Read from app1
|
||||||
const data2 = await res2.json();
|
{
|
||||||
debug('data2', data2);
|
const res = await fetch(`${apps[1].apiUrl}/user/peon-1`);
|
||||||
expect(data2).toMatchObject({
|
const data = await res.json();
|
||||||
id: "peon-1",
|
debug('data', data);
|
||||||
properties: {
|
expect(data).toMatchObject({
|
||||||
name: "Peon",
|
id: "peon-1",
|
||||||
age: 263
|
properties: {
|
||||||
}
|
name: "Peon",
|
||||||
});
|
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"]});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -169,6 +169,7 @@ export class Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
getIds(): string[] {
|
getIds(): string[] {
|
||||||
return Array.from(this.entities.keys());
|
// return Array.from(this.entities.keys());
|
||||||
|
return Array.from(this.lossless.domainEntities.keys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type User = {
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const rhizomeNode = new RhizomeNode();
|
const rhizomeNode = new RhizomeNode();
|
||||||
const users = new TypedCollection<User>("users");
|
const users = new TypedCollection<User>("user");
|
||||||
users.rhizomeConnect(rhizomeNode);
|
users.rhizomeConnect(rhizomeNode);
|
||||||
|
|
||||||
users.onUpdate((u: Entity) => {
|
users.onUpdate((u: Entity) => {
|
||||||
|
|
|
@ -97,7 +97,7 @@ export class HttpApi {
|
||||||
const html = this.mdFiles.generateIndex();
|
const html = this.mdFiles.generateIndex();
|
||||||
|
|
||||||
this.router.get('/html', (_req: express.Request, res: express.Response) => {
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ const debug = Debug('md-files');
|
||||||
|
|
||||||
const docConverter = new Converter({
|
const docConverter = new Converter({
|
||||||
completeHTMLDocument: true,
|
completeHTMLDocument: true,
|
||||||
// simpleLineBreaks: true,
|
simpleLineBreaks: false,
|
||||||
tables: true,
|
tables: true,
|
||||||
tasklists: true
|
tasklists: true
|
||||||
});
|
});
|
||||||
|
@ -27,6 +27,7 @@ export class MDFiles {
|
||||||
readme?: mdFileInfo;
|
readme?: mdFileInfo;
|
||||||
dirWatcher?: FSWatcher;
|
dirWatcher?: FSWatcher;
|
||||||
readmeWatcher?: FSWatcher;
|
readmeWatcher?: FSWatcher;
|
||||||
|
latestIndexHtml?: Html;
|
||||||
|
|
||||||
readFile(name: string) {
|
readFile(name: string) {
|
||||||
const md = readFileSync(join('./markdown', `${name}.md`)).toString();
|
const md = readFileSync(join('./markdown', `${name}.md`)).toString();
|
||||||
|
@ -69,6 +70,13 @@ export class MDFiles {
|
||||||
return htmlDocFromMarkdown(md);
|
return htmlDocFromMarkdown(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get indexHtml(): Html {
|
||||||
|
if (!this.latestIndexHtml) {
|
||||||
|
this.latestIndexHtml = this.generateIndex();
|
||||||
|
}
|
||||||
|
return this.latestIndexHtml;
|
||||||
|
}
|
||||||
|
|
||||||
readDir() {
|
readDir() {
|
||||||
// Read list of markdown files from directory and
|
// Read list of markdown files from directory and
|
||||||
// render each markdown file as html
|
// render each markdown file as html
|
||||||
|
|
Loading…
Reference in New Issue