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

Commit 4b7b8a2

Browse files
authored
mv non-trivial StorageApi methods to steroid (#395)
1 parent 44bf629 commit 4b7b8a2

File tree

8 files changed

+261
-234
lines changed

8 files changed

+261
-234
lines changed

archaeologist/src/background.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
NodeCreatedVia,
3333
UserExternalPipelineIngestionProgress,
3434
StorageApi,
35+
steroid,
3536
} from 'smuggler-api'
3637

3738
import { isReadyToBeAutoSaved } from './background/pageAutoSaving'
@@ -98,7 +99,7 @@ async function requestPageSavedStatus(url: string | undefined) {
9899
}
99100
let nodes
100101
try {
101-
nodes = await storage.node.lookup({ url })
102+
nodes = await steroid(storage).node.lookup({ url })
102103
} catch (err) {
103104
log.debug('Lookup by origin ID failed, consider page as non saved', err)
104105
return { unmemorable: false }

smuggler-api/src/api_datacenter.ts

Lines changed: 1 addition & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ import {
3131
UserExternalPipelineIngestionProgress,
3232
} from './types'
3333
import type {
34-
UniqueNodeLookupKey,
35-
NonUniqueNodeLookupKey,
36-
NodeLookupKey,
3734
CreateNodeArgs,
3835
GetNodeSliceArgs,
3936
NodeBatchRequestBody,
@@ -45,11 +42,10 @@ import { makeUrl } from './api_url'
4542

4643
import { TNodeSliceIterator, GetNodesSliceFn } from './node_slice_iterator'
4744

48-
import { genOriginId, Mime, stabiliseUrlForOriginId } from 'armoury'
45+
import { Mime } from 'armoury'
4946
import type { Optional } from 'armoury'
5047
import { MimeType, log } from 'armoury'
5148

52-
import lodash from 'lodash'
5349
import moment from 'moment'
5450
import { StatusCode } from './status_codes'
5551
import { authCookie } from './auth/cookie'
@@ -59,15 +55,6 @@ import { AuthenticationApi } from './authentication_api'
5955
const kHeaderCreatedAt = 'x-created-at'
6056
const kHeaderLastModified = 'last-modified'
6157

62-
export function isUniqueLookupKey(
63-
key: NodeLookupKey
64-
): key is UniqueNodeLookupKey {
65-
if ('nid' in key || 'webBookmark' in key) {
66-
return true
67-
}
68-
return false
69-
}
70-
7158
async function createNode(
7259
{
7360
text,
@@ -111,172 +98,6 @@ async function createNode(
11198
throw _makeResponseError(resp)
11299
}
113100

114-
function lookupKeyOf(args: CreateNodeArgs): NodeLookupKey | undefined {
115-
// TODO[snikitin@outlook.com]: This ideally should match with NodeUtil.isWebBookmark(),
116-
// NodeUtil.isWebQuote() etc but unclear how to reliably do so.
117-
if (args.extattrs?.web?.url) {
118-
return { webBookmark: { url: args.extattrs.web.url } }
119-
} else if (args.extattrs?.web_quote?.url) {
120-
return { webQuote: { url: args.extattrs.web_quote.url } }
121-
}
122-
return undefined
123-
}
124-
125-
async function createOrUpdateNode(
126-
args: CreateNodeArgs,
127-
signal?: AbortSignal
128-
): Promise<NewNodeResponse> {
129-
const lookupKey = lookupKeyOf(args)
130-
if (!lookupKey || !isUniqueLookupKey(lookupKey)) {
131-
throw new Error(
132-
`Attempt was made to create a node or, if it exists, update it, ` +
133-
`but the input node used look up key ${JSON.stringify(lookupKey)} ` +
134-
`that is not unique, which makes it impossible to correctly handle the 'update' case`
135-
)
136-
}
137-
const existingNode: TNode | undefined = await lookupNodes(lookupKey)
138-
139-
if (!existingNode) {
140-
return createNode(args, signal)
141-
}
142-
143-
const diff = describeWhatWouldPreventNodeUpdate(args, existingNode)
144-
if (diff) {
145-
throw new Error(
146-
`Failed to update node ${existingNode.nid} because some specified fields ` +
147-
`do not support update:\n${diff}`
148-
)
149-
}
150-
151-
const patch: NodePatchRequest = {
152-
text: args.text,
153-
index_text: args.index_text,
154-
}
155-
156-
await updateNode({ nid: existingNode.nid, ...patch }, signal)
157-
return { nid: existingNode.nid }
158-
}
159-
160-
/**
161-
* At the time of this writing some datapoints that can be specified at
162-
* node creation can't be modified at node update due to API differences
163-
* (@see CreateNodeArgs and @see UpdateNodeArgs).
164-
* This presents a problem for @see createOrUpdate because some of the datapoints
165-
* caller passed in will be ignored in 'update' case, which is not obvious
166-
* and would be unexpected by the caller.
167-
* As a hack this helper tries to check if these datapoints are actually
168-
* different from node's current state. If they are the same then update will
169-
* not result in anything unexpected.
170-
*/
171-
function describeWhatWouldPreventNodeUpdate(args: CreateNodeArgs, node: TNode) {
172-
let diff = ''
173-
const extattrsFieldsOfLittleConsequence = ['description']
174-
const updatableExtattrsFields = ['text', 'index_text']
175-
for (const field in args.extattrs) {
176-
const isTargetField = (v: string) => v === field
177-
const isNonUpdatable =
178-
updatableExtattrsFields.findIndex(isTargetField) === -1
179-
const isOfLittleConsequence =
180-
extattrsFieldsOfLittleConsequence.findIndex(isTargetField) !== -1
181-
if (!isNonUpdatable || isOfLittleConsequence) {
182-
continue
183-
}
184-
// @ts-ignore: No index signature with a parameter of type 'string' was found on type 'NodeExtattrs'
185-
const lhs = args.extattrs[field]
186-
// @ts-ignore: No index signature with a parameter of type 'string' was found on type 'NodeExtattrs'
187-
const rhs = node.extattrs[field]
188-
if (!lodash.isEqual(lhs, rhs)) {
189-
diff +=
190-
`\n\textattrs.${field} - ` +
191-
`${JSON.stringify(lhs)} vs ${JSON.stringify(rhs)}`
192-
}
193-
}
194-
if (args.ntype !== node.ntype) {
195-
diff += `\n\tntype - ${JSON.stringify(args.ntype)} vs ${JSON.stringify(
196-
node.ntype
197-
)}`
198-
}
199-
// At the time of this writing some datapoints that can be set on
200-
// creation of a node do not get sent back when nodes are later retrieved
201-
// from smuggler. That makes it difficult to verify if values in 'args' differ
202-
// from what's stored on smuggler side or not. A conservative validation
203-
// strategy is used ("if a value is set, treat is as an error") to cut corners.
204-
if (args.from_nid) {
205-
diff += `\n\tfrom_nid - ${args.from_nid} vs (data not exposed via smuggler)`
206-
}
207-
if (args.to_nid) {
208-
diff += `\n\tto_nid - ${args.to_nid} vs (data not exposed via smuggler)`
209-
}
210-
211-
if (!diff) {
212-
return null
213-
}
214-
215-
return `[what] - [attempted update arg] vs [existing node value]: ${diff}`
216-
}
217-
218-
async function lookupNodes(
219-
key: UniqueNodeLookupKey,
220-
signal?: AbortSignal
221-
): Promise<TNode | undefined>
222-
async function lookupNodes(
223-
key: NonUniqueNodeLookupKey,
224-
signal?: AbortSignal
225-
): Promise<TNode[]>
226-
async function lookupNodes(key: NodeLookupKey, signal?: AbortSignal) {
227-
const SLICE_ALL = {
228-
start_time: 0, // since the beginning of time
229-
bucket_time_size: 366 * 24 * 60 * 60,
230-
}
231-
if ('nid' in key) {
232-
return getNode({ nid: key.nid, signal })
233-
} else if ('webBookmark' in key) {
234-
const { id, stableUrl } = genOriginId(key.webBookmark.url)
235-
const query = { ...SLICE_ALL, origin: { id } }
236-
const iter = _getNodesSliceIter(query)
237-
238-
for (let node = await iter.next(); node != null; node = await iter.next()) {
239-
const nodeUrl = node.extattrs?.web?.url
240-
if (nodeUrl && stabiliseUrlForOriginId(nodeUrl) === stableUrl) {
241-
return node
242-
}
243-
}
244-
return undefined
245-
} else if ('webQuote' in key) {
246-
const { id, stableUrl } = genOriginId(key.webQuote.url)
247-
const query = { ...SLICE_ALL, origin: { id } }
248-
const iter = _getNodesSliceIter(query)
249-
250-
const nodes: TNode[] = []
251-
for (let node = await iter.next(); node != null; node = await iter.next()) {
252-
if (NodeUtil.isWebQuote(node) && node.extattrs?.web_quote) {
253-
if (
254-
stabiliseUrlForOriginId(node.extattrs.web_quote.url) === stableUrl
255-
) {
256-
nodes.push(node)
257-
}
258-
}
259-
}
260-
return nodes
261-
} else if ('url' in key) {
262-
const { id, stableUrl } = genOriginId(key.url)
263-
const query = { ...SLICE_ALL, origin: { id } }
264-
const iter = _getNodesSliceIter(query)
265-
266-
const nodes: TNode[] = []
267-
for (let node = await iter.next(); node != null; node = await iter.next()) {
268-
if (NodeUtil.isWebBookmark(node) && node.extattrs?.web) {
269-
if (stabiliseUrlForOriginId(node.extattrs.web.url) === stableUrl) {
270-
nodes.push(node)
271-
}
272-
}
273-
}
274-
return nodes
275-
}
276-
277-
throw new Error(`Failed to lookup nodes, unsupported key ${key}`)
278-
}
279-
280101
async function uploadFiles(
281102
{ files, from_nid, to_nid, createdVia }: BlobUploadRequestArgs,
282103
signal?: AbortSignal
@@ -963,9 +784,7 @@ export function makeDatacenterStorageApi(): StorageApi {
963784
get: getNode,
964785
update: updateNode,
965786
create: createNode,
966-
createOrUpdate: createOrUpdateNode,
967787
slice: _getNodesSliceIter,
968-
lookup: lookupNodes,
969788
delete: deleteNode,
970789
bulkDelete: bulkDeleteNodes,
971790
batch: {

smuggler-api/src/steroid/buildIndex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async function readAtMost(file: File, maxChars: number) {
3333

3434
/**
3535
* Build @see NodeIndexText from a file of any type supported in *Mazed*
36-
* (as opposed to @see smuggler.blob_index.build which is limited to file types
36+
* (as opposed to @see StorageApi.blob_index.build which is limited to file types
3737
* supported by *smuggler*)
3838
*/
3939
export async function nodeIndexFromFile(

0 commit comments

Comments
 (0)