Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: listUploads includes parts property with list of links to CARs that contain it #2347

Merged
merged 7 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 6 additions & 1 deletion packages/api/src/magic.link.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ function createMagicTestmodeBypasss () {
export const magicLinkBypassForE2ETestingInTestmode = createMagicTestmodeBypasss()

function isMagicTestModeToken (token) {
const parsed = JSON.parse(globalThis.atob(token))
let parsed
try {
parsed = JSON.parse(globalThis.atob(token))
} catch (error) {
gobengo marked this conversation as resolved.
Show resolved Hide resolved
return false
}
if (parsed.length !== 2) {
// unexpeced parse
return false
Expand Down
3 changes: 3 additions & 0 deletions packages/db/db-client-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export type UploadItem = {
created?: definitions['upload']['inserted_at']
updated?: definitions['upload']['updated_at']
content: ContentItem
backupUrls: definitions['upload']['backup_urls']
}

export type UploadItemOutput = {
Expand All @@ -218,6 +219,8 @@ export type UploadItemOutput = {
dagSize?: definitions['content']['dag_size']
pins: Array<PinItemOutput>,
deals: Array<Deal>
// array of links to things containing this Upload (e.g. CARs)
partOf: Array<string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just pluralise as parts? I like that this is similar to a partition content claim.

Copy link
Contributor Author

@gobengo gobengo Jan 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Just to check my understanding. Do we ever expect there to be more than one CAR that the Upload is sharded across? In my light testing it looked like only CARs with all blocks included. So I was thinking we could make a slightly different claim than just 'it's spread across these parts' but instead 'it's entirely within this CAR, i.e. it is a subset/partOf these CARs'.

Just now, I did make the change to call it parts as you suggested.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be multiple. If they send another CAR with the same root CID it should be added to the list.

}

export type UploadOutput = definitions['upload'] & {
Expand Down
2 changes: 1 addition & 1 deletion packages/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const uploadQuery = `
sourceCid:source_cid,
created:inserted_at,
updated:updated_at,
backupUrls:backup_urls,
content(cid, dagSize:dag_size, pins:pin(status, updated:updated_at, location:pin_location(_id:id, peerId:peer_id, peerName:peer_name, ipfsPeerId:ipfs_peer_id, region)))
`

Expand Down Expand Up @@ -555,7 +556,6 @@ export class DBClient {
// Get deals
const cids = uploads?.map((u) => u.content.cid)
const deals = await this.getDealsForCids(cids)

return {
count,
uploads: uploads?.map((u) => ({
Expand Down
1 change: 1 addition & 0 deletions packages/db/postgres/functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DROP FUNCTION IF EXISTS user_used_storage;
DROP FUNCTION IF EXISTS user_auth_keys_list;
DROP FUNCTION IF EXISTS find_deals_by_content_cids;
DROP FUNCTION IF EXISTS upsert_user;
DROP TYPE IF EXISTS stored_bytes;

-- transform a JSON array property into an array of SQL text elements
CREATE OR REPLACE FUNCTION json_arr_to_text_arr(_json json)
Expand Down
8 changes: 7 additions & 1 deletion packages/db/postgres/reset.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ DROP TABLE IF EXISTS pin_sync_request;
DROP TABLE IF EXISTS psa_pin_request;
DROP TABLE IF EXISTS content;
DROP TABLE IF EXISTS backup;
DROP TABLE IF EXISTS auth_key_history;
DROP TABLE IF EXISTS auth_key;
DROP TABLE IF EXISTS public.user;
DROP TABLE IF EXISTS user_tag;
DROP TABLE IF EXISTS user_tag_proposal;
DROP TABLE IF EXISTS email_history;
DROP TABLE IF EXISTS user_customer;
DROP TABLE IF EXISTS agreement;
DROP TABLE IF EXISTS public.user;
DROP TABLE IF EXISTS terms_of_service;

DROP TYPE IF EXISTS stored_bytes;

DROP SCHEMA IF EXISTS cargo CASCADE;
DROP SERVER IF EXISTS dag_cargo_server CASCADE;
DROP MATERIALIZED VIEW IF EXISTS public.aggregate_entry CASCADE;
Expand Down
3 changes: 3 additions & 0 deletions packages/db/postgres/tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ CREATE INDEX IF NOT EXISTS pin_sync_request_inserted_at_idx ON pin_sync_request

-- Setting search_path to public scope for uuid function(s)
SET search_path TO public;
DROP TABLE IF EXISTS psa_pin_request;
DROP extension IF EXISTS "uuid-ossp";
CREATE extension "uuid-ossp" SCHEMA public;

Expand Down Expand Up @@ -356,6 +357,8 @@ CREATE TABLE IF NOT EXISTS email_history
sent_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);


DROP VIEW IF EXISTS admin_search;
CREATE VIEW admin_search as
select
u.id::text as user_id,
Expand Down
34 changes: 34 additions & 0 deletions packages/db/test/upload.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,40 @@ describe('upload', () => {
assert.ok(userUploads.find(upload => upload.cid === sourceCid))
})

it('lists user uploads with CAR links in partOf', async () => {
const contentCid = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'
const sourceCid = 'QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR'
const exampleCarParkUrl = 'https://carpark-dev.web3.storage/bagbaiera6xcx7hiicm7sc523axbjf2otuu5nptt6brdzt4a5ulgn6qcfdwea/bagbaiera6xcx7hiicm7sc523axbjf2otuu5nptt6brdzt4a5ulgn6qcfdwea.car'
const created = new Date().toISOString()
const name = `rand-${Math.random().toString().slice(2)}`
await client.createUpload({
user: user._id,
contentCid,
sourceCid,
authKey: authKeys[0]._id,
type,
dagSize,
name,
pins: [initialPinData],
backupUrls: [`https://backup.cid/${created}`, exampleCarParkUrl],
created
})

// Default sort {inserted_at, Desc}
const { uploads } = await client.listUploads(user._id, { page: 1 })
assert.ok(uploads.length > 0)
for (const upload of uploads) {
// backupUrls raw is private
assert.ok(!('backupUrls' in upload), 'upload does not have backupUrls property')
assert.ok(Array.isArray(upload.partOf), 'upload.partOf is an array')
}
const namedUpload = uploads.find(u => u.name === name)
assert.deepEqual(namedUpload.partOf, [
// this corresponds to `exampleCarParkUrl`
gobengo marked this conversation as resolved.
Show resolved Hide resolved
'ipfs://bagbaiera6xcx7hiicm7sc523axbjf2otuu5nptt6brdzt4a5ulgn6qcfdwea'
gobengo marked this conversation as resolved.
Show resolved Hide resolved
])
})

it('can list user uploads with several options', async () => {
const { uploads: previousUserUploads, count: previousUserUploadCount } = await client.listUploads(user._id, { page: 1 })
assert(previousUserUploads, 'user has uploads')
Expand Down
24 changes: 23 additions & 1 deletion packages/db/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,39 @@
*/
export function normalizeUpload (upload) {
const nUpload = { ...upload }
const backupUrls = nUpload.backupUrls ?? []
delete nUpload.backupUrls
delete nUpload.content
delete nUpload.sourceCid

const partOf = [...carUrlsFromBackupUrls(backupUrls)]

return {
...nUpload,
...upload.content,
cid: upload.sourceCid, // Overwrite cid to source cid
pins: normalizePins(upload.content.pins, {
isOkStatuses: true
})
}),
partOf
}
}

/**
* given array of backup_urls from uploads table, return a set of ipfs:// URIs for any CAR files in the backup_urls
* @param {string[]} backupUrls
* @returns {Iterable<string>}
*/
function carUrlsFromBackupUrls (backupUrls) {
gobengo marked this conversation as resolved.
Show resolved Hide resolved
const carCIDUrls = new Set()
for (const backupUrl of backupUrls) {
// match cid v1 starting with 'ba'.
// there are also backupUrls from s3 with .car suffix and path stem is base32(multihash) (not a CID). exclude those.
const carCidFileSuffixMatch = String(backupUrl).match(/\/(ba[^/]+).car$/)
if (!carCidFileSuffixMatch) continue
carCIDUrls.add(`ipfs://${carCidFileSuffixMatch[1]}`)
}
return carCIDUrls
}

/**
Expand Down
Loading