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: use application/vnd.ipld.car as preferred content-type #1941

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion packages/api/src/routes/metaplex-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function metaplexUpload(event, ctx) {
user,
key,
car: blob,
mimeType: 'application/car',
mimeType: 'application/vnd.ipld.car',
files: [],
structure: 'Unknown',
meta,
Expand Down
4 changes: 3 additions & 1 deletion packages/api/src/routes/nfts-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ export async function nftUpload(event, ctx) {
throw new HTTPError('empty payload', 400)
}

const isCar = contentType.includes('application/car')
const isCar =
contentType.includes('application/car') ||
contentType.includes('application/vnd.ipld.car')
/** @type {'Car'|'Blob'} */
let uploadType
/** @type {DagStructure} */
Expand Down
6 changes: 3 additions & 3 deletions packages/api/src/utils/car.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CarWriter } from '@ipld/car'
/**
* @param {CID[]} roots
* @param {AsyncIterable<Block>|Iterable<Block>} blocks
* @returns {Promise<Blob & { type: 'application/car' }>}
* @returns {Promise<Blob & { type: 'application/vnd.ipld.car' }>}
*/
export const encode = async (roots, blocks) => {
const { out, writer } = CarWriter.create(roots)
Expand All @@ -22,9 +22,9 @@ export const encode = async (roots, blocks) => {
parts.push(part)
}

return /** @type {Blob & {type: 'application/car'}} */ (
return /** @type {Blob & {type: 'application/vnd.ipld.car'}} */ (
new Blob(parts, {
type: 'application/car',
type: 'application/vnd.ipld.car',
})
)
}
14 changes: 7 additions & 7 deletions packages/api/test/nfts-metaplex-upload.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('Metaplex Upload', () => {
method: 'POST',
headers: {
'x-web3auth': `Metaplex ${fixture.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
Expand All @@ -43,7 +43,7 @@ describe('Metaplex Upload', () => {
assert.strictEqual(value.cid, cid, 'Server responded with expected CID')
assert.strictEqual(
value.type,
'application/car',
'application/vnd.ipld.car',
'type should match blob mime-type'
)

Expand Down Expand Up @@ -81,7 +81,7 @@ describe('Metaplex Upload', () => {
method: 'POST',
headers: {
'x-web3auth': `Metaplex ${fixture.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
Expand All @@ -93,7 +93,7 @@ describe('Metaplex Upload', () => {
assert.strictEqual(value.cid, cid, 'Server responded with expected CID')
assert.strictEqual(
value.type,
'application/car',
'application/vnd.ipld.car',
'type should match blob mime-type'
)

Expand Down Expand Up @@ -134,7 +134,7 @@ describe('Metaplex Upload', () => {
method: 'POST',
headers: {
'x-web3auth': `Metaplex ${alteredToken}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
Expand All @@ -160,7 +160,7 @@ describe('Metaplex Upload', () => {
method: 'POST',
headers: {
'x-web3auth': `Metaplex ${alteredToken}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
Expand All @@ -183,7 +183,7 @@ describe('Metaplex Upload', () => {
method: 'POST',
headers: {
'x-web3auth': `Metaplex ${fixture.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
Expand Down
80 changes: 42 additions & 38 deletions packages/api/test/nfts-upload.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,40 +152,44 @@ describe('NFT Upload ', () => {
})

it('should upload a single CAR file', async () => {
const { root, car } = await createCar('hello world car')
// expected CID for the above data
const cid = 'bafkreifeqjorwymdmh77ars6tbrtno74gntsdcvqvcycucidebiri2e7qy'
assert.strictEqual(root.toString(), cid, 'car file has correct root')
const res = await fetch('upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': 'application/car',
},
body: car,
})

assert(res, 'Server responded')
assert(res.ok, 'Server response ok')
const { ok, value } = await res.json()
assert(ok, 'Server response payload has `ok` property')
assert.strictEqual(value.cid, cid, 'Server responded with expected CID')
assert.strictEqual(
value.type,
'application/car',
'type should match blob mime-type'
)
// check both accepted content-types for CARs
const contentTypes = ['application/car', 'application/vnd.ipld.car']
for (const contentType of contentTypes) {
const { root, car } = await createCar('hello world car')
// expected CID for the above data
const cid = 'bafkreifeqjorwymdmh77ars6tbrtno74gntsdcvqvcycucidebiri2e7qy'
assert.strictEqual(root.toString(), cid, 'car file has correct root')
const res = await fetch('upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': contentType,
},
body: car,
})

assert(res, 'Server responded')
assert(res.ok, 'Server response ok')
const { ok, value } = await res.json()
assert(ok, 'Server response payload has `ok` property')
assert.strictEqual(value.cid, cid, 'Server responded with expected CID')
assert.strictEqual(
value.type,
contentType,
'type should match request mime-type'
)

const { data } = await rawClient
.from('upload')
.select('*, content(*)')
.match({ source_cid: cid, user_id: client.userId })
.single()
const { data } = await rawClient
.from('upload')
.select('*, content(*)')
.match({ source_cid: cid, user_id: client.userId })
.single()

// @ts-ignore
assert.equal(data.source_cid, cid)
assert.equal(data.deleted_at, null)
assert.equal(data.content.dag_size, 15, 'correct dag size')
// @ts-ignore
assert.equal(data.source_cid, cid)
assert.equal(data.deleted_at, null)
assert.equal(data.content.dag_size, 15, 'correct dag size')
}
})

it('should allow a CAR with unsupported hash function', async () => {
Expand All @@ -207,9 +211,9 @@ describe('NFT Upload ', () => {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: new Blob(carBytes, { type: 'application/car' }),
body: new Blob(carBytes, { type: 'application/vnd.ipld.car' }),
})

assert(res, 'Server responded')
Expand All @@ -223,7 +227,7 @@ describe('NFT Upload ', () => {
)
assert.strictEqual(
value.type,
'application/car',
'application/vnd.ipld.car',
'type should match blob mime-type'
)
})
Expand All @@ -247,7 +251,7 @@ describe('NFT Upload ', () => {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: new Blob(carBytes),
})
Expand Down Expand Up @@ -280,13 +284,13 @@ describe('NFT Upload ', () => {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': 'application/car',
'Content-Type': 'application/vnd.ipld.car',
},
body: car,
})
const data2 = await res2.json()
assert.equal(data2.value.cid, cid)
assert.equal(data2.value.type, 'application/car', 'car')
assert.equal(data2.value.type, 'application/vnd.ipld.car', 'car')

const { data } = await rawClient
.from('upload')
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class NFTStorage {
for await (const part of car) {
carParts.push(part)
}
const carFile = new Blob(carParts, { type: 'application/car' })
const carFile = new Blob(carParts, { type: 'application/vnd.ipld.car' })
const cid = await pRetry(
async () => {
await rateLimiter()
Expand Down
2 changes: 1 addition & 1 deletion packages/client/test/lib.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('client', () => {
for await (const part of out) {
carParts.push(part)
}
const car = new Blob(carParts, { type: 'application/car' })
const car = new Blob(carParts, { type: 'application/vnd.ipld.car' })
const cid = await client.storeCar(car)
assert.equal(cid, expectedCid)
})
Expand Down
5 changes: 4 additions & 1 deletion packages/client/test/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const headers = ({ headers }) => ({
*/
const importUpload = async (request) => {
const contentType = request.headers.get('content-type') || ''
if (!contentType.includes('application/car')) {
const isCar =
contentType.includes('application/car') ||
contentType.includes('application/vnd.ipld.car')
if (!isCar) {
throw new Error(`unexpected content type: ${contentType}`)
}
const content = await request.arrayBuffer()
Expand Down
Loading