Extract serializer logic into lib
To use the same code in hubot I extracted the serializer logic into lib to later move it into `truffle-kredits` when we are done with all other common logic.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
+2
-104
@@ -1,14 +1,7 @@
|
||||
import Ember from 'ember';
|
||||
import computed from 'ember-computed';
|
||||
import injectService from 'ember-service/inject';
|
||||
|
||||
const {
|
||||
isPresent,
|
||||
} = Ember;
|
||||
|
||||
export default Ember.Object.extend({
|
||||
ipfs: injectService(),
|
||||
import EmberObject from 'ember-object';
|
||||
|
||||
export default EmberObject.extend({
|
||||
id: null,
|
||||
address: null,
|
||||
name: null,
|
||||
@@ -28,99 +21,4 @@ export default Ember.Object.extend({
|
||||
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());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
+102
-45
@@ -14,12 +14,14 @@ import Proposal from 'kredits-web/models/proposal';
|
||||
|
||||
import abis from 'contracts/abis';
|
||||
import addresses from 'contracts/addresses';
|
||||
import { ContributorSerializer } from 'kredits-web/lib/kredits';
|
||||
|
||||
const {
|
||||
getOwner,
|
||||
Logger: {
|
||||
debug,
|
||||
warn
|
||||
warn,
|
||||
error
|
||||
}
|
||||
} = 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')
|
||||
.then((contract) => contract.contributors(id))
|
||||
.then((data) => {
|
||||
debug('[kredits] contributor', data);
|
||||
// Set basic data
|
||||
.then(({
|
||||
account: address,
|
||||
hashFunction,
|
||||
hashSize,
|
||||
isCore,
|
||||
profileHash: digest,
|
||||
}) => {
|
||||
|
||||
let [ address, digest, hashFunction, size, isCore ] = data;
|
||||
let isCurrentUser = this.get('currentUserAccounts').includes(address);
|
||||
let profileHash = this.getMultihashFromBytes32({
|
||||
digest,
|
||||
hashFunction: hashFunction,
|
||||
size: size
|
||||
hashFunction,
|
||||
size: hashSize
|
||||
});
|
||||
return this.get('tokenContract')
|
||||
.then((contract) => contract.balanceOf(address))
|
||||
.then((balance) => {
|
||||
balance = balance.toNumber();
|
||||
|
||||
let contributor = getOwner(this).lookup('model:contributor');
|
||||
contributor.setProperties({
|
||||
id,
|
||||
address,
|
||||
profileHash,
|
||||
isCore,
|
||||
isCurrentUser,
|
||||
balance
|
||||
});
|
||||
// Load data from IPFS
|
||||
contributor.loadProfile();
|
||||
return contributor;
|
||||
return {
|
||||
id,
|
||||
address,
|
||||
isCore,
|
||||
isCurrentUser,
|
||||
profileHash,
|
||||
};
|
||||
})
|
||||
// Add the balance
|
||||
.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 = [];
|
||||
|
||||
for(var id = 1; id <= contributorsCount.toNumber(); id++) {
|
||||
contributors.push(this.getContributorData(id));
|
||||
contributors.push(this.getContributorById(id));
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.get('kreditsContract')
|
||||
.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')
|
||||
.storeFile(contributor.serialize())
|
||||
.then(profileHash => {
|
||||
contributor.setProperties({
|
||||
profileHash: profileHash,
|
||||
balance: 0,
|
||||
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
|
||||
});
|
||||
|
||||
let {
|
||||
digest, hashFunction, size
|
||||
} = this.getBytes32FromMultihash(profileHash);
|
||||
.storeFile(json)
|
||||
.then((profileHash) => {
|
||||
// Set new attributes
|
||||
attributes.profileHash = profileHash;
|
||||
attributes.balance = 0;
|
||||
attributes.isCurrentUser = this.get('currentUserAccounts')
|
||||
.includes(attributes.address);
|
||||
|
||||
return this.get('kreditsContract')
|
||||
.then((contract) => {
|
||||
let { address, isCore } = attributes;
|
||||
let {
|
||||
digest, hashFunction, size
|
||||
} = this.getBytes32FromMultihash(profileHash);
|
||||
|
||||
debug('[kredits] addContributor', address, digest, hashFunction, size, isCore);
|
||||
return contract.addContributor(
|
||||
contributor.address,
|
||||
address,
|
||||
digest,
|
||||
hashFunction,
|
||||
size,
|
||||
contributor.isCore
|
||||
isCore
|
||||
);
|
||||
})
|
||||
.then((data) => {
|
||||
debug('[kredits] add contributor response', data);
|
||||
return contributor;
|
||||
});
|
||||
})
|
||||
.then((data) => {
|
||||
debug('[kredits] add contributor response', data);
|
||||
return this.buildContributor(attributes);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -301,11 +357,12 @@ export default Service.extend({
|
||||
.then((contract) => {
|
||||
return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
|
||||
.then((id) => {
|
||||
id = id.toNumber();
|
||||
// check if the user is a contributor or not
|
||||
if( id.toNumber() === 0) {
|
||||
if (id === 0) {
|
||||
return RSVP.resolve();
|
||||
} else {
|
||||
return this.getContributorData(id.toNumber());
|
||||
return this.getContributorById(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -1,6 +1,4 @@
|
||||
import { moduleFor, test } from 'ember-qunit';
|
||||
import schemas from 'npm:kosmos-schemas';
|
||||
import tv4 from 'npm:tv4';
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
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']));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user