Move all metadata to IPFS with proper schemas #16

Merged
raucao merged 23 commits from feature/ipfs-metadata into master 2017-06-08 22:28:15 +00:00
9 changed files with 154 additions and 72 deletions
Showing only changes of commit 33e81a7668 - Show all commits
+43 -31
View File
@@ -1,41 +1,58 @@
import Ember from 'ember';
galfert commented 2017-06-08 13:40:36 +00:00 (Migrated from github.com)
Review

I think this is supposed to be newContributor.url instead of newContributor.name.

I think this is supposed to be `newContributor.url` instead of `newContributor.name`.
galfert commented 2017-06-08 13:43:45 +00:00 (Migrated from github.com)
Review

Not sure about this, but maybe it makes sense to add kredits.web3Instance as dependent key as well? So the property gets recalculated when kredits.web3Instance changes. Thinking of that race condition where the user provided web3 is only available after a while.

Not sure about this, but maybe it makes sense to add `kredits.web3Instance` as dependent key as well? So the property gets recalculated when `kredits.web3Instance` changes. Thinking of that race condition where the user provided web3 is only available after a while.
galfert commented 2017-06-08 15:03:56 +00:00 (Migrated from github.com)
Review

I think you missed this one.

I think you missed this one.
import Contributor from 'kredits-web/models/contributor';
export default Ember.Component.extend({
const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
id: null,
realName: null,
address: null,
ipfsHash: null,
isCore: false,
export default Component.extend({
kredits: service(),
newContributor: null,
inProgress: false,
isValidId: function() {
return Ember.isPresent(this.get('id'));
}.property('id'),
isValidRealName: function() {
return Ember.isPresent(this.get('realName'));
}.property('realName'),
isValidAddress: function() {
return this.get('kredits.web3Instance').isAddress(this.get('address'));
}.property('address'),
return this.get('kredits.web3Instance')
.isAddress(this.get('newContributor.address'));
}.property('newContributor.address'),
isValid: function() {
return this.get('isValidId') && this.get('isValidRealName') && this.get('isValidAddress');
}.property('isValidAddress', 'isValidId', 'isValidRealName'),
isValidName: function() {
return isPresent(this.get('newContributor.name'));
}.property('newContributor.name'),
isValidURL: function() {
return isPresent(this.get('newContributor.url'));
}.property('newContributor.name'),
isValidGithubUID: function() {
return isPresent(this.get('newContributor.github_uid'));
}.property('newContributor.github_uid'),
isValidGithubUsername: function() {
return isPresent(this.get('newContributor.github_username'));
}.property('newContributor.github_username'),
isValidWikiUsername: function() {
return isPresent(this.get('newContributor.wiki_username'));
}.property('newContributor.wiki_username'),
isValid: computed.and(
'isValidAddress',
'isValidName',
bumi commented 2017-06-08 21:02:31 +00:00 (Migrated from github.com)
Review

could we validate these using some json schema validators?

could we validate these using some json schema validators?
raucao commented 2017-06-08 21:07:23 +00:00 (Migrated from github.com)
Review

In theory yes (there's e.g. "format": "uri"), but it'd be much easier here to just have some simple regexp for HTTP URIs imo (which also keeps the build size down).

In theory yes (there's e.g. `"format": "uri"`), but it'd be much easier here to just have some simple regexp for HTTP URIs imo (which also keeps the build size down).
'isValidGithubUID'
),
reset: function() {
this.setProperties({
id: null,
realName: null,
address: null,
ipfsHash: null,
isCore: true,
newContributor: Contributor.create({ kind: 'person' }),
inProgress: false
});
},
actions: {
@@ -49,12 +66,7 @@ export default Ember.Component.extend({
if (this.get('isValid')) {
this.set('inProgress', true);
this.get('kredits').addContributor(
this.get('address'),
this.get('realName'),
this.get('isCore'),
this.get('id')
).then(contributor => {
this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
this.reset();
this.get('contributors').pushObject(contributor);
window.scroll(0,0);
+44 -16
View File
@@ -1,31 +1,59 @@
<form {{action "save" on="submit"}}>
newContributor: {{newContributor.kredits}}
<p>
{{input type="checkbox" name="is-core" id="is-core" checked=isCore}}
{{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
<label for="is-core" class="checkbox">
Core team member (can add contributors)
</label>
</p>
<p>
{{input name="id"
type="text"
placeholder="GitHub UID (123)"
value=id
class=(if isValidId 'valid' '')}}
</p>
<p>
{{input name="realname"
type="text"
placeholder="GitHub username"
value=realName
class=(if isValidRealName 'valid' '')}}
</p>
<p>
{{input name="address"
type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=address
value=newContributor.address
class=(if isValidAddress 'valid' '')}}
</p>
<p>
<select required onchange={{action (mut newContributor.kind) value="target.value"}}>
<option value="person" selected={{eq newContributor.kind "person"}}>Person</option>
<option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option>
</select>
</p>
<p>
{{input name="name"
type="text"
placeholder="Name"
value=newContributor.name
class=(if isValidName 'valid' '')}}
</p>
<p>
{{input name="url"
type="text"
placeholder="URL"
value=newContributor.url
class=(if isValidURL 'valid' '')}}
</p>
<p>
{{input name="github_uid"
type="text"
placeholder="GitHub UID (123)"
value=newContributor.github_uid
class=(if isValidGithubUID 'valid' '')}}
</p>
<p>
{{input name="github_username"
type="text"
placeholder="GitHub username"
value=newContributor.github_username
class=(if isValidGithubUsername 'valid' '')}}
</p>
<p>
{{input name="wiki_username"
type="text"
placeholder="Wiki Username"
value=newContributor.wiki_username
class=(if isValidWikiUsername 'valid' '')}}
</p>
<p class="actions">
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
</p>
+36 -11
View File
@@ -3,8 +3,12 @@ import Ember from 'ember';
export default Ember.Object.extend({
address: null,
name: null,
kind: null,
url: null,
github_username: null,
github_uid: null,
wiki_username: null,
ipfsHash: null,
kredits: null,
isCore: false,
@@ -14,18 +18,39 @@ export default Ember.Object.extend({
return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
}.property('github_uid'),
toJSON() {
let contributor = {
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
"kind": this.get('kind'),
"name": this.get('name'),
"accounts": []
};
if (Ember.isPresent(this.get('url'))) {
contributor["url"] = this.get('url');
}
if (Ember.isPresent(this.get('github_uid'))) {
contributor.accounts.push({
"site": "github.com",
"uid": this.get('github_uid'),
"username": this.get('github_username'),
"url": `https://github.com/${this.get('github_username')}`
});
}
if (Ember.isPresent(this.get('wiki_username'))) {
contributor.accounts.push({
"site": "wiki.kosmos.org",
"username": this.get('wiki_username'),
"url": `https://wiki.kosmos.org/User:${this.get('wiki_username')}`
});
}
return contributor;
},
serialize() {
return JSON.stringify({
profiles: {
'github.com': {
uid: this.get('github_uid'),
username: this.get('github_username'),
}
// 'wiki.kosmos.org': {
// username: this.get('wiki_username')
// }
}
});
return JSON.stringify(this.toJSON());
}
});
+3 -1
View File
@@ -1,4 +1,5 @@
import Ember from 'ember';
import Contributor from 'kredits-web/models/contributor';
export default Ember.Route.extend({
@@ -10,7 +11,8 @@ export default Ember.Route.extend({
return Ember.RSVP.hash({
contributors: kredits.getContributors(),
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
proposals: kredits.getProposals()
proposals: kredits.getProposals(),
newContributor: Contributor.create({ kind: 'person' })
});
}
+5 -10
View File
@@ -175,23 +175,18 @@ export default Service.extend({
});
},
addContributor(address, name, isCore, id) {
Ember.Logger.debug('[kredits] add contributor', name, address);
addContributor(contributor) {
Ember.Logger.debug('[kredits] add contributor', contributor);
let contributor = Contributor.create({
address: address,
github_username: name,
github_uid: id,
contributor.setProperties({
kredits: 0,
isCore: isCore,
isCurrentUser: this.get('currentUserAccounts').includes(address)
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
});
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
contributor.set('ipfsHash', ipfsHash);
Ember.Logger.debug('ADD', address, name, ipfsHash, isCore, id);
this.get('kreditsContract').addContributor(address, name, ipfsHash, isCore, id, (err, data) => {
this.get('kreditsContract').addContributor(contributor.address, contributor.name, contributor.ipfsHash, contributor.isCore, contributor.github_uid, (err, data) => {
if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] add contributor response', data);
resolve(contributor);
+2 -2
View File
@@ -54,8 +54,8 @@
</header>
<div class="content">
{{add-contributor kredits=kredits
contributors=model.contributors
{{add-contributor contributors=model.contributors
newContributor=model.newContributor
contractInteractionEnabled=contractInteractionEnabled}}
</div>
</section>
+1
View File
@@ -23,6 +23,7 @@ module.exports = function(environment) {
},
browserify: {
tests: true,
transform: [
["babelify", {
presets: ["es2015"],
+2
View File
@@ -55,8 +55,10 @@
"ember-resolver": "^2.0.3",
"ember-truth-helpers": "1.3.0",
"ipfs-api": "^12.1.7",
"kosmos-schemas": "~1.1.0",
"kredits-contracts": "^2.4.0",
"loader.js": "^4.0.10",
"tv4": "^1.3.0",
"web3": "^0.18.2"
},
"engines": {
+18 -1
View File
@@ -1,9 +1,26 @@
import { moduleFor, test } from 'ember-qunit';
import schemas from 'npm:kosmos-schemas';
import tv4 from 'npm:tv4';
moduleFor('model:contributor', 'Unit | Model | contributor');
test('avatarURL returns correct URL', function(assert) {
test('#avatarURL() returns correct URL', function(assert) {
let model = this.subject();
model.set('github_uid', '318');
assert.equal(model.get('avatarURL'), 'https://avatars2.githubusercontent.com/u/318?v=3&s=128');
});
test('#toJSON() returns a valid JSON-LD representation of the model', function(assert) {
let model = this.subject();
model.setProperties({
name: 'Satoshi Nakamoto',
kind: 'person',
github_uid: 123,
github_username: 'therealsatoshi',
wiki_username: 'Satoshi',
});
assert.ok(tv4.validate(model.toJSON(), schemas['contributor']));
});