Skip to content

Commit

Permalink
Issue #10: create DrupalUser and MeteorUser instances, refactored not…
Browse files Browse the repository at this point in the history
…ifications.
  • Loading branch information
fgm committed Jul 6, 2017
1 parent 78e8d14 commit 30e366c
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 29 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
'DrupalConfiguration': true,
'DrupalServer': true,
'DrupalUser': true,
'MeteorUser': true,
'client': true,
'drupal': true,
'server': true,
Expand Down
1 change: 1 addition & 0 deletions package.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Package.onUse(function (api) {
const sharedPre = [
'DrupalBase',
'DrupalUser',
'MeteorUser',
'sharedBootPre'
];

Expand Down
103 changes: 76 additions & 27 deletions server/DrupalServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,17 +508,6 @@ DrupalServer = class DrupalServer extends DrupalBase {
const EVENT_NO_PARAM = 0;
const EVENT_HAS_PARAMS = 1;

// If there is any kind of delay, ensure it is a strictly positive integer.
function getValidDelay(query) {
const DEFAULT_DELAY = 1000;

let usDelay = query.delay;
const delay = (typeof usDelay !== 'undefined')
? parseInt(usDelay, 10) || DEFAULT_DELAY
: 0;
return delay;
}

function getEventType(event) {
const validEvents = {
// Drupal 8 user hooks.
Expand Down Expand Up @@ -548,25 +537,80 @@ DrupalServer = class DrupalServer extends DrupalBase {
/**
* Extract the base64-encoded, JSON-serialized update data.
*
* Do not convert to a named function to preserve the "this" lexical scope.
*
* @param {object} query
* Expected to hold the update in a base64-encoded, JSON-serialized value
* for the "data" key.
*
* @returns {*}
* The deserialized data. May be null if no valid data key was found.
*/
function getPayload(query) {
const getPayload = query => {
const rawData = query.data || '';
const decoded = Buffer.from(rawData, 'base64').toString();
const deserialized = decoded === '' ? null : JSON.parse(decoded);
let deserialized = null;

if (decoded === '') {
return deserialized;
}

try {
deserialized = JSON.parse(decoded);
}
catch (e) {
if (e instanceof SyntaxError) {
this.logger.info('Received ill-formed payload', { decoded });
}
else {
this.logger.warning('Unexpected exception decoding rawQuery', { rawQuery });
}
}

return deserialized;
};

// If there is any kind of delay, ensure it is a strictly positive integer.
function getValidDelay(query) {
const DEFAULT_DELAY = 1000;

let usDelay = query.delay;
const delay = (typeof usDelay !== 'undefined')
? parseInt(usDelay, 10) || DEFAULT_DELAY
: 0;
return delay;
}

/**
* Notify client of change.
*
* Always use a timeout: it will be 0 if none was provided in the update.
*
* Do not convert to a named function to preserve the "this" lexical scope.
*
* @param {Error} error
* @param {Object} pushedUpdate
* The update to apply.
*
* @returns {void}
*/
const pushUpdate = (error, pushedUpdate) => {
if (error) {
this.logger.warn(error.message, { error });
return;
}

Meteor.setTimeout(() => {
pushedUpdate.insertedAt = new Date();
this.updatesCollection.insert(pushedUpdate);
}, pushedUpdate.delay || 0);
};

const query = rawQuery || {};
const delay = getValidDelay(query);
const event = String(query.event);
const userId = parseInt(query.userId, 10) || 0;

const delay = getValidDelay(query);
const eventType = getEventType(event);
if (eventType === EVENT_INVALID) {
this.logger.warn('Invalid update request, ignored.', { delay, event, userId });
Expand All @@ -580,23 +624,17 @@ DrupalServer = class DrupalServer extends DrupalBase {
userId
};
this.logger.debug('Storing update', update);
const pushUpdate = () => {
// Always use a timeout: it will be 0 if none was provided anyway.
Meteor.setTimeout(() => {
update.insertedAt = new Date();
this.updatesCollection.insert(update);
}, delay);
};

if (eventType === EVENT_HAS_PARAMS) {
const payload = getPayload(query);
if (payload === null) {
this.logger.info('Empty update request, ignored', { delay, event, userId });
return;
}
this.userUpdate(userId, payload, pushUpdate);
this.userUpdate(userId, payload, update, pushUpdate);
}
else {
pushUpdate();
pushUpdate(null, update);
}
}

Expand All @@ -623,15 +661,26 @@ DrupalServer = class DrupalServer extends DrupalBase {
* The Drupal UID.
* @param {object} payload
* The Drupal-provided information to build the user upsert.
* @param {Object} update
* The update object.
* @param {function} done
* Called after update.
*
* @returns {void}
*/
userUpdate(userId, payload, done) {
const user = new DrupalUser();
console.log(user);
done();
userUpdate(userId, payload, update, done) {
// Ensure it is not missing from payload.
payload.userId = parseInt(userId, 10) || 0;

const rawMatch = MeteorUser.findByUserId(payload.userId, this.usersCollection);
if (rawMatch) {
const drupalUser = new DrupalUser(payload);
MeteorUser.updateFromDrupal(rawMatch, drupalUser);
done(null, update);
}
else {
done(new Error('Cannot update non-existent user.'), null);
}
}

/**
Expand Down
7 changes: 5 additions & 2 deletions shared/DrupalUser.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @file
* Contains the DrupalServer class.
* Contains the DrupalUser class.
*/

Log.debug('Defining shared/DrupalUser');
Expand All @@ -11,7 +11,10 @@ Log.debug('Defining shared/DrupalUser');
* @type {DrupalUser}
*/
DrupalUser = class DrupalUser {
constructor() {
constructor(data) {
for (const item of Object.keys(data)) {
this[item] = data[item];
}
}
};

Expand Down
97 changes: 97 additions & 0 deletions shared/MeteorUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @file
* Contains the MeteorUser class.
*/

Log.debug('Defining shared/DrupalUser');

/**
* A class providing the mechanisms for the Meteor side of Drupal accounts.
*
* @type {MeteorUser}
*/
MeteorUser = class MeteorUser {
/**
* @constructor
*
* @param {DrupalUser} drupalUser
* The Drupal user to reflect to the Meteor database.
*/
constructor(drupalUser) {
const anome = Meteor.settings['accounts-drupal'].anonymousName;
const defaultProfile = this.makeProfile(anome);
this._id = new MongoID();
this.createdAt = new Date();
this.services = {
'accounts-drupal': {
id: anome,
public: defaultProfile,
onProfile: defaultProfile,
offProfile: defaultProfile,
},
resume: {
loginTokens: [],
}
};
this.profile = {
'accounts-drupal': defaultProfile,
};
this.username = anome;
this.emails = [];

for (const item of Object.keys(data)) {
this[item] = data[item];
}
}

makeProfile(anome, data) {
if (typeof data === 'undefined') {
return {
uid: 0,
name: anome,
displayName: anome,
roles: [],
};
}

let result;
return result;
}

/**
* Find the existing collection record for a given Drupal userID.
*
* @param {int} userId
* The userID to look for.
* @param {Collection} users
* The Meteor users collection.
*
* @returns {bool|Object}
* The database document if found, false otherwise.
*/
static findByUserId(userId, users) {
const currentRaw = users.findOne({
[MeteorUser.USER_KEY]: userId,
}) || false;
return currentRaw;
}

/**
* Update an existing collection record from a DrupalUser instance.
*
* @param {DrupalUser} drupalUser
* @param {Collection} users
*/
static updateFromDrupal(drupalUser, users) {
}

/**
* The name of the key used to locate records by Drupal userID.
*
* @returns {string}
* The key.
*/
static get USER_KEY() {
return 'services.accounts-drupal.public.uid';
}
};

0 comments on commit 30e366c

Please sign in to comment.