Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

js-git-ipld <-> pando library #53

Closed
wants to merge 9 commits into from
Closed
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
179 changes: 179 additions & 0 deletions packages/pando-repository/app/src/lib/pando.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { IPLD } from 'ipld'
import IPFS from 'ipfs-http-client'
import IPLDGit from 'ipld-git'
import CID from 'cids'
import { cidToSha } from 'ipld-git/src/util/util.js'
import orderBy from 'lodash.orderby'
import uniqWith from 'lodash.uniqwith'

const ipfs = IPFS({ host: 'ipfs.infura.io', port: '5001', protocol: 'https' })
const ipld = new IPLD({ blockService: ipfs.block, formats: [IPLDGit] })

export interface IAuthorOrCommitter {
name: string
email: string
date: string
}

export interface ITree { [path: string]: string }

export interface ILinkedDataCommit {
author: IAuthorOrCommitter
committer: IAuthorOrCommitter
message: string
parents: ITree[] | null
Copy link
Member

Choose a reason for hiding this comment

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

Isn't it supposed to be : parents: ILinkedCommitData[] ?

tree: ITree
}

export interface IModifiedTree {
[path: string]: {
lastEdit: {
date: string
message: string
}
mode: string | number
blob: string
}
}

export const fetchFilesFromTree = (hash: string, path?: string) => {
return new Promise(async (resolve, reject) => {
let files = {}
const cid = new CID(hash)

ipld.get(cid, async (err, result) => {
if (err) {
reject(err)
} else {
const tree = result.value
for (let entry in tree) {
const _path = path ? path + '/' + entry : entry
if (tree[entry].mode === '40000') {
const _files = await fetchFilesFromTree(tree[entry]['hash']['/'], _path)
files = { ...files, ..._files }
} else {
files[_path].lastEdit = {
date: tree[entry]['committer'].date,
message: tree[entry]['message']
}
files[_path].mode = tree[entry].mode
files[_path].blob = new CID(tree[entry]['hash']['/']).toBaseEncodedString()
}
}
resolve(files as IModifiedTree)
}
})
})
}

export const fetchHistory = async (hash:string, history: Commit[]): Promise<Commit[]> => {
return new Promise(async (resolve, reject) => {
try {
const commit = await fetchCommit(hash)
history.push(commit)

for (let parent of commit.parents) {
history = await fetchHistory(parent['/'], history)
}

resolve(history)
} catch (err) {
reject(err)
}
})
}

export const fetchCommit = async (hash: string): Promise<Commit> => {
return new Promise((resolve, reject) => {
try {
const cid = new CID(hash)
ipld.get(cid, async (err, result) => {
if (err) {
reject(err)
} else {
const IPLDCommit = result.value as ILinkedDataCommit
const commit = new Commit(IPLDCommit)
await commit.fetchModifiedTree()
resolve(commit)
}
})
} catch (err) {
reject(err)
}
})
}

export const fetchOrderedHistory = async (hash: string, formerHistory: Commit[]) => {
let history = await fetchHistory(hash, formerHistory)
history = uniqWith(history, (one, two) => {
return one.cid === two.cid
})

history = orderBy(
history,
[
commit => {
// https://github.com/git/git/blob/v2.3.0/Documentation/date-formats.txt
/* eslint-disable-next-line no-unused-vars */
const [timestamp, offset] = commit.author.date.split(' ')
return timestamp
},
],
['desc']
)

return history
}

export default class Commit {
cid: string
sha: string
author: IAuthorOrCommitter
committer: IAuthorOrCommitter
message: string
parents: ITree[] | string[]
tree: ITree | IModifiedTree | {}

constructor(commit: ILinkedDataCommit) {
if (commit.tree.hasOwnProperty('/')) {
const _cid = new CID(commit.tree['/'])
let _parents : string[] = commit.parents ?
commit.parents.map(parent => (new CID(Object.values(parent)[0]).toBaseEncodedString)) : []
this.cid = _cid
this.sha = cidToSha(commit.tree['/']).toString('hex')
this.author = commit.author
this.committer = commit.committer
this.message = commit.message
this.tree = commit.tree
this.parents = _parents
}
}

public async fetchModifiedTree(): Promise<void> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I came upon this solution given that we couldn't traverse the tree inside the constructor

return new Promise(async (resolve, reject) => {
try {
const files = await fetchFilesFromTree(this.tree['/'])
this.tree = files
resolve()
} catch (err) {
reject(err)
}
})

}

public async getOrderdHistory(): Promise<Commit[]> {
return new Promise(async (resolve, reject) => {
try {
const orderedHistory = await fetchOrderedHistory(this.cid, [])
resolve(orderedHistory)
} catch (err) {
reject(err)
}
})
}
}

//export function convertCommitToIPLDCommit(commit: Commit): ILinkedDataCommit {}


39 changes: 23 additions & 16 deletions packages/pando-repository/app/src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,7 @@ app.store(async (state, event) => {
switch (event.event) {
case 'UpdateRef':
try {
let history = await fetchHistory(event.returnValues.hash, [])
history = uniqWith(history, (one, two) => {
return one.cid === two.cid
})
history = orderBy(
history,
[
commit => {
// https://github.com/git/git/blob/v2.3.0/Documentation/date-formats.txt
/* eslint-disable-next-line no-unused-vars */
const [timestamp, offset] = commit.author.date.split(' ')
return timestamp
},
],
['desc']
)
let history = await fetchOrderedHistory(event.returnValues.hash, [])
state.branches[branchFromRef(event.returnValues.ref)] = history
} catch (err) {
console.error('Failed to load commit history due to:', err)
Expand Down Expand Up @@ -156,3 +141,25 @@ const fetchFilesFromTree = (hash, path) => {
})
})
}

const fetchOrderedHistory = async (hash, formerHistory) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the final version of this PR, we'll deprecate all of these helper functions in this file and import them from pando.ts

let history = await fetchHistory(hash, formerHistory)
history = uniqWith(history, (one, two) => {
return one.cid === two.cid
})

history = orderBy(
history,
[
commit => {
// https://github.com/git/git/blob/v2.3.0/Documentation/date-formats.txt
/* eslint-disable-next-line no-unused-vars */
const [timestamp, offset] = commit.author.date.split(' ')
return timestamp
},
],
['desc']
)

return history
}
11 changes: 8 additions & 3 deletions packages/pando-repository/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"lint:contracts": "solium --dir ./contracts",
"lint:tests": "eslint ./test",
"lint:app": "cd app && npm run lint",
"lint": "npm run lint:contracts && npm run lint:tests && npm run lint:app",
"lint": "npm run lint:contracts && npm run lint:tests && npm run lint:app && tslint -c tslint.json 'app/src/**/*.ts'",
"compile": "aragon contracts compile",
"test": "aragon contracts test --network devchain",
"start": "cd app && npm run start",
Expand All @@ -26,11 +26,13 @@
"publish:major:rinkeby": "aragon apm publish major --use-frame --files ./app/build --environment rinkeby"
},
"dependencies": {
"@aragon/os": "^4.0.1"
"@aragon/os": "^4.0.1",
"ipld-git": "^0.3.0"
},
"devDependencies": {
"@aragon/cli": "^5.5.0",
"@aragon/test-helpers": "^1.1.0",
"@types/node": "^10.12.21",
"eslint": "5.5.0",
"eslint-config-prettier": "^3.0.1",
"eslint-config-standard": "^12.0.0",
Expand All @@ -41,7 +43,10 @@
"eslint-plugin-standard": "^4.0.0",
"ethlint": "^1.2.3",
"prettier": "^1.8.2",
"truffle": "^5.0.0"
"truffle": "^5.0.0",
"tslint": "^5.14.0",
"tslint-config-prettier": "^1.18.0",
"typescript": "^3.4.3"
},
"gitHead": "cb2b7c6fe7805fdbbcd29733c3ebb24eeb5d3d42"
}
10 changes: 10 additions & 0 deletions packages/pando-repository/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"exclude": ["node_modules", "**/*.spec.ts"],
"compilerOptions": {
"esModuleInterop": true,
"module": "CommonJS",
"outDir": "./build",
"target": "ES2017",
"allowJs": true
}
}
4 changes: 4 additions & 0 deletions packages/pando-repository/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": { "curly": [true, "ignore-same-line"], "interface-name": false, "variable-name": false }
}