Call Back Query: Promises vs guide callback pattern


#1

Hi,

What is the best way folks have found to handle callbacks? In essence, say I have some method to insert something like so…

export const insertLib = new ValidatedMethod({
  name: 'libs.insert',
  validate: new SimpleSchema({
    libName: { type: String },
    libType: { type: String },
    libEntities: { type: [String] },
    libAccess: { type: Object, blackbox: true },
    libFolders: { type: Array },
    'libFolders.$': { type: Object, blackbox: true },
  }).validator(),
  run({ libName, libType, libEntities, libAccess, libFolders }) {
    if (Meteor.isServer) {
      return Libs.insert({ libName, libType, libEntities, libAccess, libFolders });
    }
  },
});

So then my helper would look like…

export const initLib = (entityId, userId, libName) => {
  check(userId, String);
  check(entityId, String);

  const rootFolder = {
    folderId: guid(), // Random folder id
    folderName: libName,
    folderParent: 'Root',
    folderChildren: [],
    folderData: [],
  };

  const newLib = {
    libName: libName,
    libType: 'User',
    libEntities: [entityId],
    libAccess:
    {
      libR: [userId],
      libW: [userId],
      libX: [userId],
    },
    libFolders: [rootFolder],
  };

  const newLibId = insertLib.call(newLib); // Return undefined in code....

  editEntity(entityId, 'addLib', { lib: newLibId }); // THIS IS WHERE I HAVE THE PROBLEM

  return newLibId;
};

I can’t seem to get code like below working for some reason…

let newLibId = '';
newLibId = insertLib.call(newLib, (err, result) => {
  if (err) {
    throw new Meteor.Error(err);
  } 

  editEntity(entityId, 'addLib', { lib: result });

  return result; // newLibId does not get set to result.
});

I’ve been trying to use Promises, but not having a lot of luck. Any pointers? Don’t care if I get the callbacks working properly or use promises, whatever is easier. Also, according to this smart people are trying to solve this problem. Not being smart enough to understand the tradeoff’s, but enough to know I need a solution, what is the consensus of the best way to proceed? Is there a consensus?

Also, none of these seem to fail in test’s for some reason (unit tests of functions), but fail when tested by a user. Have not yet got around to shallow rendering components to see what happens. Why is this the case?

Thanks so much.

Tat

Tests which work fine below. Unfortunately they seem to not pick up the error.

describe('Add Lib Helpers', function () {
  beforeEach(function () {
    if (Meteor.isServer) {
      resetDatabase();
    }
  });

  if (Meteor.isServer) {

    it('initLib: Helper init\'s a lib on user creation', function() {
      const testEntity = {
        entityName: 'Test Entity',
      };

      const testEntityId = addEntity(testEntity);
      assert.typeOf(testEntityId, 'string');

      const testUser = {
        email: 'test@test.com',
        password: 'password',
        profile: {
          name: { first: 'Test', last: 'User' },
        },
        roles: {
          type: ['admin'],
          group: 'q6a',
        },
        entity: testEntityId,
      };

      // Add the user
      const testUserId = initUser(testUser);
      assert.typeOf(testUserId, 'string');

      const testUserLibId = initLib(testEntityId, testUserId, 'Test User');
      assert.typeOf(testUserLibId, 'string');
      assert.equal(getLib(testUserLibId).libEntities.includes(testEntityId), true);

      // Add a Group Library
      const testGroupLibId = addGroupLib(testEntityId, 'Some Group');
      assert.typeOf(testGroupLibId, 'string');
      assert.equal(getLib(testGroupLibId).libEntities.includes(testEntityId), true);

      assert.equal(getEntityLibs(testEntityId).includes(testGroupLibId), true);
    });

  }
});

#2

HI,

Ok - so I tried a few things. Does the below look legit?

  1. Have a normal method for the insert.
  2. Low level helper functions handle data back and forth. They end with…
export const addGroupLib2 = (entityId, libName) => {
  const rootFolder = {
    folderId: guid(), 
    folderName: libName,
    folderParent: 'Root',
    folderChildren: [],
    folderData: [],
  };

  const newGroupLib = {
    libName: libName,
    libType: 'Group',
    libEntities: [entityId],
    libAccess:
    {
      libR: [],
      libW: [],
      libX: [],
    },
    libFolders: [rootFolder],
  };
  
  return new Promise((resolve, reject) => { 
    insertLib.call(newGroupLib, (err, result) => {
      if(err) {
        reject (err);
      } else {
        resolve(result);
      }
    });
  });
};

Higher level biz logic functions look like so…

export async function addGroupLibPromise (entityId, libName) {
  try {
    var newGroupLibId = await addGroupLib2(entityId, libName);
    await editEntityAddLib(entityId, newGroupLibId);
  } catch (error) {
    throw new Meteor.Error(error);
  }
}

Is this legit? Where should I putting the promises (or returning them anyways). The thinking is that the lower level helpers all return promises, and the biz logic functions deal with these promises only?

Thanks so much.

Tat

P.S. Any ideas on how to test this in a non-shite pattern?