Skip to content

Commit

Permalink
feat(apple): store latest_updates in database (#6)
Browse files Browse the repository at this point in the history
- Add `latest_updates` table to database
- Use timestamp of latest update in `Last-Modified` header when downloading .pkpass
- Use timestamp of latest update as `lastUpdated` when getting the list of updatable passes
  • Loading branch information
aahna-ashina committed Sep 11, 2022
1 parent 832c734 commit 21032c2
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 66 deletions.
4 changes: 2 additions & 2 deletions server/cypress/e2e/api/apple/[passTypeIdentifier].cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ describe('Get the List of Updatable Passes', () => {
})
})

it('200 when existing deviceLibraryIdentifier and passesUpdatedSince=v1', () => {
it('200 when existing deviceLibraryIdentifier and passesUpdatedSince=1662541136', () => {
cy.request({
method: 'GET',
url: '/api/apple/v1/devices/cypress_b33e3a3dccb3030333e3333da33333a3/registrations/pass.org.passport.nation3?passesUpdatedSince=v1',
url: '/api/apple/v1/devices/cypress_b33e3a3dccb3030333e3333da33333a3/registrations/pass.org.passport.nation3?passesUpdatedSince=1662541136',
failOnStatusCode: false
}).then((response) => {
expect(response.status).to.eq(200)
Expand Down
7 changes: 7 additions & 0 deletions server/db-migrations/0.9.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create table latest_updates (
id bigint generated by default as identity primary key,
time timestamp without time zone default now() not null,
title text not null,
content text not null
);
insert into latest_updates (title, content) values ('N3GOV-16: All governance proposals must explain how they help advance the North Star metrics', 'This is very self-descriptive, but it''s a nice add-on to Proposal: Set Nation3''s North Star metrics. We have seen too many times how DAOs end up with endless bureaucracy because there''s no room for execution amidst chaos and uncertainty. Therefore having a unified set of metrics to advance, and making all governance proposals adhere to advancing them, makes sense. This is the pull request to the governance proposals repo, and this is the specific change.')
4 changes: 2 additions & 2 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mobile-passport",
"version": "0.8.0",
"version": "0.9.0",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {

// Expected URL format:
// /api/apple/v1/devices/[deviceLibraryIdentifier]/registrations/[passTypeIdentifier]?passesUpdatedSince=[previousLastUpdated]
// /api/apple/v1/devices/b33e3a3dccb3030333e3333da33333a3/registrations/pass.org.passport.nation3?passesUpdatedSince=v1
// /api/apple/v1/devices/b33e3a3dccb3030333e3333da33333a3/registrations/pass.org.passport.nation3?passesUpdatedSince=1662541136
console.log('req.url:', req.url)

try {
Expand Down Expand Up @@ -51,11 +51,29 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
// There are no matching passes
res.status(204).end()
} else {
// Return matching passes (serial numbers)
res.status(200).json({
serialNumbers: serialNumbers,
lastUpdated: `v${config.appleTemplateVersion}`
})
// Lookup the latest update and its timestamp
supabase
.from('latest_updates')
.select('*')
.order('time', { ascending: false })
.limit(1)
.single()
.then((latest_updates_result: any) => {
console.log('latest_updates_result:', latest_updates_result)
if (latest_updates_result.error) {
res.status(500).json({
error: 'Internal Server Error: ' + latest_updates_result.error.message
})
} else {
const latestUpdateDate: Date = new Date(latest_updates_result.data['time'])

// Return matching passes (serial numbers)
res.status(200).json({
serialNumbers: serialNumbers,
lastUpdated: Math.round(latestUpdateDate.getTime() / 1000)
})
}
})
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,60 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
.single()
.then((result: any) => {
console.log('result:', result)

if (result.error) {
res.status(500).json({
error: 'Internal Server Error: ' + result.error.message
})
} else {
// Populate the pass template
const platform: Platform = Platform.Apple
const templateVersion: number = config.appleTemplateVersion
const passportID: string = String(serialNumber)
const issueDate: Date = new Date(result.data['issue_date'])
const issueDateTimestamp: number = Math.round(issueDate.getTime() / 1000)
const address: string = result.data['address']
const ensName: string = result.data['ens_name']
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
issueDateTimestamp,
address,
ensName
)
console.log('filePath:', filePath)

// Return the updated pass
const fileName = `passport_${address}_v${templateVersion}.pkpass`
console.log('fileName:', fileName)
res.setHeader('Last-Modified', `v${templateVersion}`)
res.setHeader('Content-Disposition', `attachment;filename=${fileName}`)
res.setHeader('Content-Type', 'application/vnd.apple.pkpass')
res.setHeader('Content-Length', fs.statSync(filePath).size)
const readStream = fs.createReadStream(filePath)
readStream.pipe(res)
// Lookup the latest update and its timestamp
supabase
.from('latest_updates')
.select('*')
.order('time', { ascending: false })
.limit(1)
.single()
.then((latest_updates_result: any) => {
console.log('latest_updates_result:', latest_updates_result)
if (latest_updates_result.error) {
res.status(500).json({
error: 'Internal Server Error: ' + latest_updates_result.error.message
})
} else {
const latestUpdateDate: Date = new Date(latest_updates_result.data['time'])
const latestUpdateTitle: string = latest_updates_result.data['title']
const latestUpdateContent: string = latest_updates_result.data['content']

// Populate the pass template
const platform: Platform = Platform.Apple
const templateVersion: number = config.appleTemplateVersion
const passportID: string = String(serialNumber)
const issueDate: Date = new Date(result.data['issue_date'])
const issueDateTimestamp: number = Math.round(issueDate.getTime() / 1000)
const address: string = result.data['address']
const ensName: string = result.data['ens_name']
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
issueDateTimestamp,
address,
ensName,
latestUpdateTitle,
latestUpdateContent
)
console.log('filePath:', filePath)

// Return the updated pass
const fileName = `passport_${address}_v${templateVersion}.pkpass`
console.log('fileName:', fileName)
res.setHeader('Last-Modified', Math.round(latestUpdateDate.getTime() / 1000))
res.setHeader('Content-Disposition', `attachment;filename=${fileName}`)
res.setHeader('Content-Type', 'application/vnd.apple.pkpass')
res.setHeader('Content-Length', fs.statSync(filePath).size)
const readStream = fs.createReadStream(filePath)
readStream.pipe(res)
}
})
}
})
} catch (err: any) {
Expand Down
64 changes: 42 additions & 22 deletions server/pages/api/downloadPass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,28 +119,48 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
error: 'Internal Server Error: ' + result.error.message
})
} else {
// Populate the pass template
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
timestamp,
address,
ensName
)
console.log('filePath:', filePath)

// Serve the pass download to the user
const fileName = `passport_${address}_v${templateVersion}.pkpass`
console.log('fileName:', fileName)
res.setHeader(
'Content-Disposition',
`attachment;filename=${fileName}`
)
res.setHeader('Content-Type', 'application/vnd.apple.pkpass')
res.setHeader('Content-Length', fs.statSync(filePath).size)
const readStream = fs.createReadStream(filePath)
readStream.pipe(res)
// Lookup the latest update and its timestamp
supabase
.from('latest_updates')
.select('*')
.order('time', { ascending: false })
.limit(1)
.single()
.then((latest_updates_result: any) => {
console.log('latest_updates_result:', latest_updates_result)
if (latest_updates_result.error) {
res.status(500).json({
error: 'Internal Server Error: ' + latest_updates_result.error.message
})
} else {
const latestUpdateDate: Date = new Date(latest_updates_result.data['time'])
const latestUpdateTitle: string = result.data['title']
const latestUpdateContent: string = result.data['content']

// Populate the pass template
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
timestamp,
address,
ensName,
latestUpdateTitle,
latestUpdateContent
)
console.log('filePath:', filePath)

// Serve the pass download to the user
const fileName = `passport_${address}_v${templateVersion}.pkpass`
console.log('fileName:', fileName)
res.setHeader('Last-Modified', Math.round(latestUpdateDate.getTime() / 1000))
res.setHeader('Content-Disposition', `attachment;filename=${fileName}`)
res.setHeader('Content-Type', 'application/vnd.apple.pkpass')
res.setHeader('Content-Length', fs.statSync(filePath).size)
const readStream = fs.createReadStream(filePath)
readStream.pipe(res)
}
})
}
})
})
Expand Down
12 changes: 10 additions & 2 deletions server/utils/Passes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ test('generatePass - Apple v1', () => {
const issueDateTimestamp: number = 1662541136 // September 7, 2022 8:58:56 AM
const address: string = '0x394b00B5De4E6f30292aCaC37f810Dd0672E211E'
const ensName: string = 'vitalik.eth'
const latestUpdateTitle: string = 'N3GOV-15: Set Nation3\’s North Star metrics'
const latestUpdateContent: string = 'A North Star metric is paramount since it aligns everyone in the nation towards a particular, measurable goal.'
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
issueDateTimestamp,
address,
ensName
ensName,
latestUpdateTitle,
latestUpdateContent
)
expect(filePath).toContain('passport_0x394b00B5De4E6f30292aCaC37f810Dd0672E211E.pkpass')
expect(fs.existsSync(filePath)).toBe(true)
Expand All @@ -31,13 +35,17 @@ test('generatePass - Apple v2', () => {
const issueDateTimestamp: number = 1662541136 // September 7, 2022 8:58:56 AM
const address: string = '0x394b00B5De4E6f30292aCaC37f810Dd0672E211E'
const ensName: string = 'vitalik.eth'
const latestUpdateTitle: string = 'N3GOV-15: Set Nation3\’s North Star metrics'
const latestUpdateContent: string = 'A North Star metric is paramount since it aligns everyone in the nation towards a particular, measurable goal.'
const filePath: string = Passes.generatePass(
platform,
templateVersion,
passportID,
issueDateTimestamp,
address,
ensName
ensName,
latestUpdateTitle,
latestUpdateContent
)
expect(filePath).toContain('passport_0x394b00B5De4E6f30292aCaC37f810Dd0672E211E.pkpass')
expect(fs.existsSync(filePath)).toBe(true)
Expand Down
10 changes: 7 additions & 3 deletions server/utils/Passes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export class Passes {
passportID: string,
issueDateTimestamp: number,
holderAddress: any,
holderENSName: string
holderENSName: string,
latestUpdateTitle: string,
latestUpdateContent: string
): string {
console.log('generatePass')

Expand All @@ -31,6 +33,8 @@ export class Passes {
console.log('issueDateTimestamp:', issueDateTimestamp)
console.log('holderAddress:', holderAddress)
console.log('holderENSName:', holderENSName)
console.log('latestUpdateTitle:', latestUpdateTitle)
console.log('latestUpdateContent:', latestUpdateContent)

if (platform == Platform.Apple) {
// Create temporary directory for storing the pass files
Expand Down Expand Up @@ -95,8 +99,8 @@ export class Passes {

if (templateVersion >= 3) {
// Set "Latest Nation3 Update" (title and content)
passJson.storeCard.backFields[5].value = 'N3GOV-16: All governance proposals must explain how they help advance the North Star metrics'
passJson.storeCard.backFields[6].value = 'This is very self-descriptive, but it\'s a nice add-on to Proposal: Set Nation3\'s North Star metrics. We have seen too many times how DAOs end up with endless bureaucracy because there\'s no room for execution amidst chaos and uncertainty. Therefore having a unified set of metrics to advance, and making all governance proposals adhere to advancing them, makes sense. This is the pull request to the governance proposals repo, and this is the specific change.'
passJson.storeCard.backFields[5].value = latestUpdateTitle
passJson.storeCard.backFields[6].value = latestUpdateContent
}
}

Expand Down

0 comments on commit 21032c2

Please sign in to comment.