Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.

Commit 94d411d

Browse files
committed
implement edge.* and pass ts checks
1 parent 6a0759d commit 94d411d

File tree

3 files changed

+150
-14
lines changed

3 files changed

+150
-14
lines changed

archaeologist/src/storage_api_local.ts

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@
1010
import {
1111
Ack,
1212
AddUserActivityRequest,
13+
AddUserExternalAssociationRequest,
1314
AdvanceExternalPipelineIngestionProgress,
15+
CreateEdgeArgs,
1416
CreateNodeArgs,
17+
EdgeUtil,
1518
Eid,
1619
GetNodeSliceArgs,
1720
NewNodeResponse,
1821
Nid,
1922
NodeBatch,
2023
NodeBatchRequestBody,
2124
NodeCreatedVia,
25+
NodeEdges,
2226
NodePatchRequest,
2327
NodeUtil,
2428
OriginId,
2529
StorageApi,
30+
TEdge,
2631
TEdgeJson,
2732
TNode,
2833
TNodeJson,
@@ -89,6 +94,12 @@ type Lav =
8994
| OriginToActivityLav
9095
| ExtPipelineLav
9196

97+
type YekLav = { yek: Yek; lav: Lav }
98+
99+
function isOfArrayKind(lav: Lav): lav is OriginToNidLav | NidToEdgeLav {
100+
return Array.isArray(lav.lav.value)
101+
}
102+
92103
// TODO[snikitin@outlook.com] Describe that the purpose of this wrapper is to
93104
// add a bit of ORM-like typesafety to browser.Storage.StorageArea.
94105
// Without this it's very difficult to keep track of what the code is doing
@@ -100,7 +111,7 @@ class YekLavStore {
100111
this.store = store
101112
}
102113

103-
set(items: { yek: Yek; lav: Lav }[]): Promise<void> {
114+
set(items: YekLav[]): Promise<void> {
104115
for (const item of items) {
105116
if (item.yek.yek.kind !== item.lav.lav.kind) {
106117
throw new Error(
@@ -125,6 +136,7 @@ class YekLavStore {
125136
get(yek: NidYek[]): Promise<NidLav[]>
126137
get(yek: OriginToActivityYek): Promise<OriginToActivityLav | undefined>
127138
get(yek: ExtPipelineYek): Promise<ExtPipelineLav | undefined>
139+
get(yek: Yek): Promise<Lav | undefined>
128140
get(yek: Yek | Yek[]): Promise<Lav | Lav[] | undefined> {
129141
if (Array.isArray(yek)) {
130142
const keys: string[] = yek.map((value: Yek) => this.stringify(value))
@@ -150,6 +162,49 @@ class YekLavStore {
150162
)
151163
}
152164

165+
// TODO[snikitin@outlook.com] Explain that this method is a poor man's attempt
166+
// to increase atomicity of data insertion
167+
async prepareAppend(
168+
yek: OriginToNidYek,
169+
lav: OriginToNidLav
170+
): Promise<{
171+
yek: OriginToNidYek
172+
lav: OriginToNidLav
173+
}>
174+
async prepareAppend(
175+
yek: NidToEdgeYek,
176+
lav: NidToEdgeLav
177+
): Promise<{
178+
yek: NidToEdgeYek
179+
lav: NidToEdgeLav
180+
}>
181+
async prepareAppend(yek: Yek, appended_lav: Lav): Promise<YekLav> {
182+
if (yek.yek.kind !== appended_lav.lav.kind) {
183+
throw new Error(
184+
`Attempted to append a key/value pair of mismatching kinds: '${yek.yek.kind}' !== '${appended_lav.lav.kind}'`
185+
)
186+
}
187+
const lav = await this.get(yek)
188+
if (lav != null && !isOfArrayKind(lav)) {
189+
throw new Error(`prepareAppend only works/makes sense for arrays`)
190+
}
191+
const value = lav?.lav.value ?? []
192+
// TODO[snikitin@outlook.com] I'm sure it's possible to convince Typescript
193+
// that below is safe, but don't know how
194+
return {
195+
yek,
196+
// @ts-ignore Type '{...}' is not assignable to type 'Lav'
197+
lav: {
198+
lav: {
199+
kind: yek.yek.kind,
200+
// @ts-ignore Each member of the union type has signatures, but none of those
201+
// signatures are compatible with each other
202+
value: value.concat(appended_lav.lav.value),
203+
},
204+
},
205+
}
206+
}
207+
153208
private stringify(yek: Yek): string {
154209
switch (yek.yek.kind) {
155210
case 'nid':
@@ -221,11 +276,10 @@ async function createNode(
221276
const yek: OriginToNidYek = {
222277
yek: { kind: 'origin->nid', key: args.origin },
223278
}
224-
let lav: OriginToNidLav | undefined = await store.get(yek)
225-
lav = lav ?? { lav: { kind: 'origin->nid', value: [] } }
226-
const nidsWithThisOrigin: Nid[] = lav.lav.value
227-
nidsWithThisOrigin.push(node.nid)
228-
records.push({ yek, lav })
279+
const lav: OriginToNidLav = {
280+
lav: { kind: 'origin->nid', value: [node.nid] },
281+
}
282+
records.push(await store.prepareAppend(yek, lav))
229283
}
230284

231285
if (from_nid.length > 0 || to_nid.length > 0) {
@@ -316,6 +370,67 @@ async function updateNode(
316370
return { ack: true }
317371
}
318372

373+
async function createEdge(
374+
store: YekLavStore,
375+
args: CreateEdgeArgs
376+
): Promise<TEdge> {
377+
// TODO[snikitin@outlook.com] Evaluate if ownership support is needed
378+
// and implement if yes
379+
const owned_by = 'todo'
380+
381+
const createdAt: number = unixtime.now()
382+
const edge: TEdgeJson = {
383+
eid: generateEid(),
384+
from_nid: args.from,
385+
to_nid: args.to,
386+
crtd: createdAt,
387+
upd: createdAt,
388+
is_sticky: false,
389+
owned_by,
390+
}
391+
392+
const items: YekLav[] = []
393+
{
394+
const yek: NidToEdgeYek = { yek: { kind: 'nid->edge', key: args.from } }
395+
const lav: NidToEdgeLav = { lav: { kind: 'nid->edge', value: [edge] } }
396+
items.push(await store.prepareAppend(yek, lav))
397+
}
398+
{
399+
const reverseEdge: TEdgeJson = {
400+
...edge,
401+
from_nid: args.to,
402+
to_nid: args.from,
403+
}
404+
const yek: NidToEdgeYek = { yek: { kind: 'nid->edge', key: args.to } }
405+
const lav: NidToEdgeLav = {
406+
lav: { kind: 'nid->edge', value: [reverseEdge] },
407+
}
408+
items.push(await store.prepareAppend(yek, lav))
409+
}
410+
await store.set(items)
411+
return EdgeUtil.fromJson(edge)
412+
}
413+
414+
async function getNodeAllEdges(
415+
store: YekLavStore,
416+
nid: string
417+
): Promise<NodeEdges> {
418+
const yek: NidToEdgeYek = { yek: { kind: 'nid->edge', key: nid } }
419+
const lav: NidToEdgeLav | undefined = await store.get(yek)
420+
const ret: NodeEdges = { from_edges: [], to_edges: [] }
421+
if (lav == null) {
422+
return ret
423+
}
424+
for (const edge of lav.lav.value) {
425+
if (edge.to_nid === nid) {
426+
ret.from_edges.push(EdgeUtil.fromJson(edge))
427+
} else {
428+
ret.to_edges.push(EdgeUtil.fromJson(edge))
429+
}
430+
}
431+
return ret
432+
}
433+
319434
async function addExternalUserActivity(
320435
store: YekLavStore,
321436
origin: OriginId,
@@ -461,10 +576,10 @@ export function makeLocalStorageApi(
461576
},
462577
},
463578
edge: {
464-
// create: createEdge,
465-
// get: getNodeAllEdges,
466-
// sticky: switchEdgeStickiness,
467-
// delete: deleteEdge,
579+
create: (args: CreateEdgeArgs) => createEdge(store, args),
580+
get: (nid: string, _signal?: AbortSignal) => getNodeAllEdges(store, nid),
581+
sticky: throwUnimplementedError('edge.sticky'),
582+
delete: throwUnimplementedError('edge.delete'),
468583
},
469584
activity: {
470585
external: {
@@ -477,8 +592,21 @@ export function makeLocalStorageApi(
477592
getExternalUserActivity(store, origin),
478593
},
479594
association: {
480-
// record: recordExternalAssociation,
481-
// get: getExternalAssociation,
595+
// TODO[snikitin@outlook.com] Replace stubs with real implementation
596+
record: (
597+
_origin: {
598+
from: OriginId
599+
to: OriginId
600+
},
601+
_body: AddUserExternalAssociationRequest,
602+
_signal?: AbortSignal
603+
) => Promise.resolve({ ack: true }),
604+
get: (
605+
{}: {
606+
origin: OriginId
607+
},
608+
_signal?: AbortSignal
609+
) => Promise.resolve({ from: [], to: [] }),
482610
},
483611
},
484612
external: {

smuggler-api/src/storage_api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ export type BlobUploadRequestArgs = {
5959
}
6060

6161
export type CreateEdgeArgs = {
62-
from?: Nid
63-
to?: Nid
62+
from: Nid
63+
to: Nid
6464
signal: AbortSignal
6565
}
6666

smuggler-api/src/typesutil.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
TNode,
66
TNodeJson,
77
TEdge,
8+
TEdgeJson,
89
} from './types'
910
import { AccountInterface } from './auth/account_interface'
1011

@@ -92,4 +93,11 @@ export namespace EdgeUtil {
9293
account?.isAuthenticated() && account?.getUid() === edge.owned_by
9394
return res || false
9495
}
96+
export function fromJson(edge: TEdgeJson): TEdge {
97+
return {
98+
...edge,
99+
crtd: moment.unix(edge.crtd),
100+
upd: moment.unix(edge.upd),
101+
}
102+
}
95103
}

0 commit comments

Comments
 (0)