Refactor contributor model #30

Merged
fsmanuel merged 4 commits from refactor/models into master 2018-04-07 17:22:34 +00:00
13 changed files with 360 additions and 264 deletions
+41 -55
View File
@@ -1,81 +1,67 @@
import Ember from 'ember'; import Component from 'ember-component';
import Contributor from 'kredits-web/models/contributor'; import computed, { and } from 'ember-computed';
import injectService from 'ember-service/inject';
import isPresent from 'kredits-web/utils/cps/is-present';
const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
export default Component.extend({ export default Component.extend({
kredits: injectService(),
kredits: service(), // Default attributes used by reset
attributes: {
address: null,
name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
wiki_username: null,
isCore: false,
},
newContributor: null, didInsertElement() {
inProgress: false, this._super(...arguments);
this.reset();
},
isValidAddress: function() { isValidAddress: computed('kredits.ethProvider', 'address', function() {
// TODO: add proper address validation // TODO: add proper address validation
return this.get('newContributor.address') !== ''; return this.get('address') !== '';
}.property('kredits.ethProvider', 'newContributor.address'), }),
isValidName: isPresent('name'),
isValidName: function() { isValidURL: isPresent('url'),
return isPresent(this.get('newContributor.name')); isValidGithubUID: isPresent('github_uid'),
}.property('newContributor.name'), isValidGithubUsername: isPresent('github_username'),
isValidWikiUsername: isPresent('wiki_username'),
isValidURL: function() { isValid: and(
return isPresent(this.get('newContributor.url'));
}.property('newContributor.url'),
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', 'isValidAddress',
'isValidName', 'isValidName',
'isValidGithubUID' 'isValidGithubUID'
), ),
reset: function() { reset: function() {
this.setProperties({ this.setProperties(this.get('attributes'));
newContributor: Contributor.create({ kind: 'person' }),
inProgress: false
});
}, },
actions: { actions: {
submit() {
save() { if (!this.get('isValid')) {
if (!this.get('contractInteractionEnabled')) { alert('Invalid data. Please review and try again.');
alert('Only core team members can add new contributors. Please ask someone to set you up.');
return; return;
} }
if (this.get('isValid')) { let attributes = Object.keys(this.get('attributes'));
this.set('inProgress', true); let contributor = this.getProperties(attributes);
let saved = this.save(contributor);
this.get('kredits').addContributor(this.get('newContributor')).then(contributor => { // The promise handles inProgress
this.set('inProgress', saved);
saved.then(() => {
this.reset(); this.reset();
this.get('contributors').pushObject(contributor);
window.scroll(0,0); window.scroll(0,0);
window.alert('Contributor added.');
}); });
} else {
alert('Invalid data. Please review and try again.');
} }
} }
}
}); });
+17 -12
View File
@@ -1,6 +1,9 @@
<form {{action "save" on="submit"}}> <form {{action "submit" on="submit"}}>
<p> <p>
{{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}} {{input name="is-core"
type="checkbox"
id="is-core"
checked=isCore}}
<label for="is-core" class="checkbox"> <label for="is-core" class="checkbox">
Core team member (can add contributors) Core team member (can add contributors)
</label> </label>
@@ -9,51 +12,53 @@
{{input name="address" {{input name="address"
type="text" type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=newContributor.address value=address
class=(if isValidAddress 'valid' '')}} class=(if isValidAddress 'valid' '')}}
</p> </p>
<p> <p>
<select required onchange={{action (mut newContributor.kind) value="target.value"}}> <select required onchange={{action (mut kind) value="target.value"}}>
<option value="person" selected={{eq newContributor.kind "person"}}>Person</option> <option value="person" selected={{eq kind "person"}}>Person</option>
<option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option> <option value="organization" selected={{eq kind "organization"}}>Organization</option>
</select> </select>
</p> </p>
<p> <p>
{{input name="name" {{input name="name"
type="text" type="text"
placeholder="Name" placeholder="Name"
value=newContributor.name value=name
class=(if isValidName 'valid' '')}} class=(if isValidName 'valid' '')}}
</p> </p>
<p> <p>
{{input name="url" {{input name="url"
type="text" type="text"
placeholder="URL" placeholder="URL"
value=newContributor.url value=url
class=(if isValidURL 'valid' '')}} class=(if isValidURL 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_uid" {{input name="github_uid"
type="text" type="text"
placeholder="GitHub UID (123)" placeholder="GitHub UID (123)"
value=newContributor.github_uid value=github_uid
class=(if isValidGithubUID 'valid' '')}} class=(if isValidGithubUID 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_username" {{input name="github_username"
type="text" type="text"
placeholder="GitHub username" placeholder="GitHub username"
value=newContributor.github_username value=github_username
class=(if isValidGithubUsername 'valid' '')}} class=(if isValidGithubUsername 'valid' '')}}
</p> </p>
<p> <p>
{{input name="wiki_username" {{input name="wiki_username"
type="text" type="text"
placeholder="Wiki Username" placeholder="Wiki Username"
value=newContributor.wiki_username value=wiki_username
class=(if isValidWikiUsername 'valid' '')}} class=(if isValidWikiUsername 'valid' '')}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}} {{input type="submit"
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
</p> </p>
</form> </form>
+8 -3
View File
@@ -116,13 +116,18 @@ export default Ember.Controller.extend({
actions: { actions: {
confirmProposal(proposalId) { confirmProposal(proposalId) {
this.get('kredits').vote(proposalId).then(transaction => { this.get('kredits').vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash); window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
}); });
} },
save(contributor) {
return this.get('kredits').addContributor(contributor)
.then((contributor) => {
this.get('model.contributors').pushObject(contributor);
return contributor;
});
}
} }
}); });
+5
View File
@@ -0,0 +1,5 @@
import ContributorSerializer from './serializers/contributor';
export {
ContributorSerializer
};
@@ -0,0 +1,93 @@
/**
* Handle serialization for JSON-LD object of the contributor, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
*
* @class
* @public
*/
export default class Contributor {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
let {
name,
kind,
url,
accounts,
} = JSON.parse(serialized);
let github_username, github_uid, wiki_username;
let github = accounts.find((a) => a.site === 'github.com');
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
if (github) {
({ username: github_username, uid: github_uid} = github);
}
if (wiki) {
({ username: wiki_username } = wiki);
}
return {
name,
kind,
url,
github_uid,
github_username,
wiki_username,
ipfsData: serialized,
};
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
let {
name,
kind,
url,
github_uid,
github_username,
wiki_username,
} = deserialized;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
kind,
name,
"accounts": []
};
if (url) {
data["url"] = url;
}
if (github_uid) {
data.accounts.push({
"site": "github.com",
"uid": github_uid,
"username": github_username,
"url": `https://github.com/${github_username}`
});
}
if (wiki_username) {
data.accounts.push({
"site": "wiki.kosmos.org",
"username": wiki_username,
"url": `https://wiki.kosmos.org/User:${wiki_username}`
});
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
}
+3 -105
View File
@@ -1,14 +1,7 @@
import Ember from 'ember';
import computed from 'ember-computed'; import computed from 'ember-computed';
import injectService from 'ember-service/inject'; import EmberObject from 'ember-object';
const {
isPresent,
} = Ember;
export default Ember.Object.extend({
ipfs: injectService(),
export default EmberObject.extend({
id: null, id: null,
address: null, address: null,
name: null, name: null,
@@ -18,7 +11,7 @@ export default Ember.Object.extend({
github_uid: null, github_uid: null,
wiki_username: null, wiki_username: null,
profileHash: null, profileHash: null,
balance: null, balance: 0,
isCore: false, isCore: false,
isCurrentUser: false, isCurrentUser: false,
@@ -28,99 +21,4 @@ export default Ember.Object.extend({
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`; return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
} }
}), }),
/**
* Loads the contributor's profile data from IPFS and sets local instance
* properties from it
*
* @method
* @public
*/
loadProfile() {
let profileHash = this.get('profileHash');
if (!profileHash) {
return;
}
return this.get('ipfs')
.getFile(profileHash)
.then((content) => {
let profile = Ember.Object.create(JSON.parse(content));
this.set('ipfsData', JSON.stringify(profile, null, 2));
Ember.Logger.debug('[contributor] loaded contributor profile', profile);
this.setProperties({
name: profile.get('name'),
kind: profile.get('kind')
});
let accounts = profile.get('accounts');
let github = accounts.findBy('site', 'github.com');
let wiki = accounts.findBy('site', 'wiki.kosmos.org');
if (isPresent(github)) {
this.setProperties({
github_username: github.username,
github_uid: github.uid,
});
}
if (isPresent(wiki)) {
this.setProperties({
wiki_username: wiki.username
});
}
}).catch((err) => {
Ember.Logger.error('[contributor] error trying to load contributor profile', profileHash, err);
});
},
/**
* Creates a JSON-LD object of the contributor, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
*
* @method
* @public
*/
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;
},
/**
* Returns the JSON-LD representation of the model as a string
*
* @method
* @public
*/
serialize() {
return JSON.stringify(this.toJSON());
}
}); });
+97 -40
View File
@@ -14,12 +14,14 @@ import Proposal from 'kredits-web/models/proposal';
import abis from 'contracts/abis'; import abis from 'contracts/abis';
import addresses from 'contracts/addresses'; import addresses from 'contracts/addresses';
import { ContributorSerializer } from 'kredits-web/lib/kredits';
const { const {
getOwner, getOwner,
Logger: { Logger: {
debug, debug,
warn warn,
error
} }
} = Ember; } = Ember;
@@ -103,37 +105,55 @@ export default Service.extend({
}); });
}, },
getContributorData(id) { // TODO: Should be part of the service
buildContributor(attributes) {
debug('[kredits] buildContributor', attributes);
let contributor = getOwner(this).lookup('model:contributor');
contributor.setProperties(attributes);
return contributor;
},
getContributorById(id) {
return this.get('contributorsContract') return this.get('contributorsContract')
.then((contract) => contract.contributors(id)) .then((contract) => contract.contributors(id))
.then((data) => { // Set basic data
debug('[kredits] contributor', data); .then(({
account: address,
hashFunction,
hashSize,
isCore,
profileHash: digest,
}) => {
let [ address, digest, hashFunction, size, isCore ] = data;
let isCurrentUser = this.get('currentUserAccounts').includes(address); let isCurrentUser = this.get('currentUserAccounts').includes(address);
let profileHash = this.getMultihashFromBytes32({ let profileHash = this.getMultihashFromBytes32({
digest, digest,
hashFunction: hashFunction, hashFunction,
size: size size: hashSize
}); });
return this.get('tokenContract')
.then((contract) => contract.balanceOf(address))
.then((balance) => {
balance = balance.toNumber();
let contributor = getOwner(this).lookup('model:contributor'); return {
contributor.setProperties({
id, id,
address, address,
profileHash,
isCore, isCore,
isCurrentUser, isCurrentUser,
balance profileHash,
}); };
// Load data from IPFS })
contributor.loadProfile(); // Add the balance
return contributor; .then((data) => {
return this.get('tokenContract')
.then((contract) => contract.balanceOf(data.address))
.then((balance) => {
data.balance = balance.toNumber();
return data;
}); });
})
// Fetch IPFS data if available
.then(this.loadContributorProfile.bind(this))
.then((attributes) => {
return this.buildContributor(attributes);
}); });
}, },
@@ -145,13 +165,43 @@ export default Service.extend({
let contributors = []; let contributors = [];
for(var id = 1; id <= contributorsCount.toNumber(); id++) { for(var id = 1; id <= contributorsCount.toNumber(); id++) {
contributors.push(this.getContributorData(id)); contributors.push(this.getContributorById(id));
} }
return RSVP.all(contributors); return RSVP.all(contributors);
}); });
}, },
/**
* Loads the contributor's profile data from IPFS and returns the attributes
*
* @method
* @public
*/
loadContributorProfile(data) {
let profileHash = data.profileHash;
if (!profileHash) {
return data;
}
return this.get('ipfs')
.getFile(profileHash)
.then(ContributorSerializer.deserialize)
.then((attributes) => {
debug('[kredits] loaded contributor profile', attributes);
return Object.assign({}, data, attributes);
})
.catch((err) => {
error(
'[kredits] error trying to load contributor profile',
profileHash,
err
);
});
},
getProposalData(i) { getProposalData(i) {
return this.get('kreditsContract') return this.get('kreditsContract')
.then((contract) => contract.proposals(i)) .then((contract) => contract.proposals(i))
@@ -234,36 +284,42 @@ export default Service.extend({
}; };
}, },
addContributor(contributor) {
debug('[kredits] add contributor', contributor); // TODO: extract common logic to module
addContributor(attributes) {
debug('[kredits] add contributor', attributes);
let json = ContributorSerializer.serialize(attributes);
return this.get('ipfs') return this.get('ipfs')
.storeFile(contributor.serialize()) .storeFile(json)
.then(profileHash => { // Set profileHash
contributor.setProperties({ .then((profileHash) => {
profileHash: profileHash, attributes.profileHash = profileHash;
balance: 0, return attributes;
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address) })
}); .then((attributes) => {
return this.get('kreditsContract')
.then((contract) => {
let { address, isCore, profileHash } = attributes;
let { let {
digest, hashFunction, size digest, hashFunction, size
} = this.getBytes32FromMultihash(profileHash); } = this.getBytes32FromMultihash(profileHash);
return this.get('kreditsContract') let contributor = [
.then((contract) => { address,
return contract.addContributor(
contributor.address,
digest, digest,
hashFunction, hashFunction,
size, size,
contributor.isCore isCore,
); ];
debug('[kredits] addContributor', ...contributor);
return contract.addContributor(...contributor);
});
}) })
.then((data) => { .then((data) => {
debug('[kredits] add contributor response', data); debug('[kredits] add contributor response', data);
return contributor; return this.buildContributor(attributes);
});
}); });
}, },
@@ -301,11 +357,12 @@ export default Service.extend({
.then((contract) => { .then((contract) => {
return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject')) return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
.then((id) => { .then((id) => {
id = id.toNumber();
// check if the user is a contributor or not // check if the user is a contributor or not
if( id.toNumber() === 0) { if (id === 0) {
return RSVP.resolve(); return RSVP.resolve();
} else { } else {
return this.getContributorData(id.toNumber()); return this.getContributorById(id);
} }
}); });
}); });
+6 -3
View File
@@ -65,8 +65,11 @@
</header> </header>
<div class="content"> <div class="content">
{{add-contributor contributors=model.contributors {{!--
newContributor=model.newContributor TODO:
contractInteractionEnabled=contractInteractionEnabled}} only show the form if currentUser.isCore else show:
Only core team members can add new contributors. Please ask someone to set you up.
--}}
{{add-contributor contributors=model.contributors save=(action 'save')}}
</div> </div>
</section> </section>
+8
View File
@@ -0,0 +1,8 @@
import computed from 'ember-computed';
import { isPresent } from 'ember-utils';
export default function(key) {
return computed(key, function() {
return isPresent(this.get(key));
});
}
+17 -19
View File
@@ -3144,6 +3144,11 @@
"safe-buffer": "5.1.1" "safe-buffer": "5.1.1"
}, },
"dependencies": { "dependencies": {
"core-util-is": {
"version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
},
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@@ -3163,13 +3168,6 @@
"safe-buffer": "5.1.1", "safe-buffer": "5.1.1",
"string_decoder": "1.0.3", "string_decoder": "1.0.3",
"util-deprecate": "1.0.2" "util-deprecate": "1.0.2"
},
"dependencies": {
"core-util-is": {
"version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true
}
} }
}, },
"string_decoder": { "string_decoder": {
@@ -3462,6 +3460,11 @@
"source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz"
}, },
"dependencies": { "dependencies": {
"convert-source-map": {
"version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.3.0.tgz",
"integrity": "sha512-8iaBspZWViJD+h8epOgGyXGWH2wSDLzQ8w9qDs/L7W67IfnEeXKu/Q1cPeWpIrRJc9Mvzbn6hKxnL3goD5ncwQ==",
"dev": true
},
"minimatch": { "minimatch": {
"version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
"integrity": "sha512-NyXjqu1IwcqH6nv5vmMtaG3iw7kdV3g6MwlUBZkc3Vn5b5AMIWYKfptvzipoyFfhlfOgBQ9zoTxQMravF1QTnw==", "integrity": "sha512-NyXjqu1IwcqH6nv5vmMtaG3iw7kdV3g6MwlUBZkc3Vn5b5AMIWYKfptvzipoyFfhlfOgBQ9zoTxQMravF1QTnw==",
@@ -5668,11 +5671,6 @@
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true "dev": true
}, },
"convert-source-map": {
"version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.3.0.tgz",
"integrity": "sha512-8iaBspZWViJD+h8epOgGyXGWH2wSDLzQ8w9qDs/L7W67IfnEeXKu/Q1cPeWpIrRJc9Mvzbn6hKxnL3goD5ncwQ==",
"dev": true
},
"copy-dereference": { "copy-dereference": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/copy-dereference/-/copy-dereference-1.0.0.tgz", "resolved": "https://registry.npmjs.org/copy-dereference/-/copy-dereference-1.0.0.tgz",
@@ -22277,7 +22275,7 @@
"ember-cli-babel": { "ember-cli-babel": {
"version": "6.12.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz", "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
"integrity": "sha512-LMwZ3Xf3Q3jQUXaJtLLJsbbhRZRNv/iea64lZ8OgqZp1fh66CSXfmqV3L9QSuYQKPDNqFiu2v6IpOT08C6GU6w==", "integrity": "sha1-Otzb4SeNofzQuQOPE2DLSsXUQUw=",
"dev": true, "dev": true,
"requires": { "requires": {
"amd-name-resolver": "0.0.7", "amd-name-resolver": "0.0.7",
@@ -23378,7 +23376,7 @@
"ember-weakmap": { "ember-weakmap": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/ember-weakmap/-/ember-weakmap-3.1.1.tgz", "resolved": "https://registry.npmjs.org/ember-weakmap/-/ember-weakmap-3.1.1.tgz",
"integrity": "sha512-rfW3A1m3NFsHd/NuHyBkssU0Qf0zGcJmASGfhjZc7fIQeBZvSLIFYZTej+W+YBLPtED9h/SVW63DHTRY5PUR4Q==", "integrity": "sha1-KubgCAtbgM8NEI93Utxp6pYD29c=",
"dev": true, "dev": true,
"requires": { "requires": {
"browserslist": "2.11.3", "browserslist": "2.11.3",
@@ -23389,7 +23387,7 @@
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
@@ -23398,7 +23396,7 @@
"ember-cli-babel": { "ember-cli-babel": {
"version": "6.12.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz", "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
"integrity": "sha512-LMwZ3Xf3Q3jQUXaJtLLJsbbhRZRNv/iea64lZ8OgqZp1fh66CSXfmqV3L9QSuYQKPDNqFiu2v6IpOT08C6GU6w==", "integrity": "sha1-Otzb4SeNofzQuQOPE2DLSsXUQUw=",
"dev": true, "dev": true,
"requires": { "requires": {
"amd-name-resolver": "0.0.7", "amd-name-resolver": "0.0.7",
@@ -25695,7 +25693,7 @@
} }
}, },
"kredits-contracts": { "kredits-contracts": {
"version": "github:67P/truffle-kredits#4d2ba2a4bf1bb3ceb1ae9c768abfc73a9cf5ab00", "version": "github:67P/truffle-kredits#bdd99d58cf601d9fadd81c7f0cbd61e57cf552b5",
"dev": true "dev": true
}, },
"lcid": { "lcid": {
@@ -28245,7 +28243,7 @@
}, },
"slash": { "slash": {
"version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
"integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
"dev": true "dev": true
}, },
"snapdragon": { "snapdragon": {
@@ -29020,7 +29018,7 @@
}, },
"trim-right": { "trim-right": {
"version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
"integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
"dev": true "dev": true
}, },
"true-case-path": { "true-case-path": {
+1
View File
@@ -53,6 +53,7 @@
"ember-load-initializers": "^0.5.1", "ember-load-initializers": "^0.5.1",
"ember-macro-helpers": "0.17.0", "ember-macro-helpers": "0.17.0",
"ember-parachute": "0.1.0", "ember-parachute": "0.1.0",
"ember-promise-helpers": "1.0.6",
"ember-resolver": "^2.0.3", "ember-resolver": "^2.0.3",
"ember-truth-helpers": "1.3.0", "ember-truth-helpers": "1.3.0",
"ethers": "^3.0.8", "ethers": "^3.0.8",
@@ -0,0 +1,53 @@
import { module, test } from 'ember-qunit';
import schemas from 'npm:kosmos-schemas';
import tv4 from 'npm:tv4';
import { ContributorSerializer } from 'kredits-web/lib/kredits';
module('Serializers contributor');
test('#serialize returns a valid JSON-LD representation', function(assert) {
let serialized = ContributorSerializer.serialize({
name: 'Satoshi Nakamoto',
kind: 'person',
github_uid: 123,
github_username: 'therealsatoshi',
wiki_username: 'Satoshi',
});
let valid = tv4.validate(JSON.parse(serialized), schemas['contributor']);
assert.ok(valid);
});
test('#deserialize returns a valid object representation', function(assert) {
let json = JSON.stringify({
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
"kind": "person",
"name": "Satoshi Nakamoto",
"accounts": [
{
"site": "github.com",
"uid": 123,
"username": "therealsatoshi",
"url": "https://github.com/therealsatoshi"
},
{
"site": "wiki.kosmos.org",
"username": "Satoshi",
"url": "https://wiki.kosmos.org/User:Satoshi"
}
]
});
let deserialized = ContributorSerializer.deserialize(json);
let expected = {
name: 'Satoshi Nakamoto',
kind: 'person',
github_uid: 123,
github_username: 'therealsatoshi',
wiki_username: 'Satoshi',
url: undefined,
ipfsData: json,
};
assert.deepEqual(expected, deserialized);
});
-16
View File
@@ -1,6 +1,4 @@
import { moduleFor, test } from 'ember-qunit'; import { moduleFor, test } from 'ember-qunit';
import schemas from 'npm:kosmos-schemas';
import tv4 from 'npm:tv4';
moduleFor('model:contributor', 'Unit | Model | contributor'); moduleFor('model:contributor', 'Unit | Model | contributor');
@@ -10,17 +8,3 @@ test('#avatarURL() returns correct URL', function(assert) {
assert.equal(model.get('avatarURL'), 'https://avatars2.githubusercontent.com/u/318?v=3&s=128'); 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']));
});