Can someone update the tutorial to be compatible with @maidsafe/safe-node-app 0.9.0 API please?


#1

The new API is confusing to me.
I’m shooting in the dark with how all things work.
I have this so far:

safenetwork.js

let prms;

async function authoriseAndConnect() {
  prms = safe.initialiseApp({
    name: 'Hello SAFE Network',
    id: 'net.maidsafe.tutorials.web-app',
    version: '0.1.0',
    vendor: 'MaidSafe.net Ltd.'
  })
  const containers = { '_videos': ['Read'], '_pictures' : ['Read', 'Insert']}
  console.log('Authorising SAFE application...');
  prms.then(app => app.auth.genAuthUri(containers)
	.then(uri => app.auth.openUri(uri)))
  console.log("Application connected to the network")
}

async function createMutableData() {
  const typeTag = 15000
  prms.then(app => app.mutableData.newRandomPublic(typeTag)
    .then((mData) => mData.quickSetup({keyA: 'input value'})))
}

async function getItems() {
  let items = [];
  return items
}

async function insertItem(key, value) {
}

async function updateItem(key, value, version) {
}

async function removeItems(items) {
}

module.exports = {
  authoriseAndConnect,
  createMutableData,
  getItems,
  insertItem,
  updateItem,
  removeItems
}

A shot in the dark, but at least it builds.
It’s not without errors in the console though.

peruse console

:5000/#/:1 Uncaught (in promise) E {message: "Setup Incomplete. Connection not available yet.", stack: "1001↵    at module.exports (/opt/Maidsafe/Peruse/r…alhost:5000/index.js:19682:83)↵    at <anonymous>"}message: "Setup Incomplete. Connection not available yet."stack: "1001↵    at module.exports (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:32:10)↵    at SAFEApp.get connection [as connection] (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/app.js:189:13)↵    at MutableDataInterface.newEntries (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:640:42)↵    at MutableData.quickSetup (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:283:33)↵    at app.mutableData.newRandomPublic.then.mData (http://localhost:5000/index.js:19682:83)↵    at <anonymous>"__proto__: Error
    at makeError (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:23:17)
    at module.exports (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:31:13)
    at SAFEApp.get connection [as connection] (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/app.js:189:13)
    at MutableDataInterface.newEntries (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:640:42)
    at MutableData.quickSetup (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:283:33)
    at app.mutableData.newRandomPublic.then.mData (http://localhost:5000/index.js:19682:83)
    at <anonymous>code: 1001constructor: function E(message)arguments: nullcaller: nulllength: 1name: "E"prototype: Error
    at makeError (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:23:17)
    at module.exports (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:31:13)
    at SAFEApp.get connection [as connection] (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/app.js:189:13)
    at MutableDataInterface.newEntries (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:640:42)
    at MutableData.quickSetup (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:283:33)
    at app.mutableData.newRandomPublic.then.mData (http://localhost:5000/index.js:19682:83)
    at <anonymous>__proto__: function ()[[FunctionLocation]]: /opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:15[[Scopes]]: Scopes[4]name: 1001stack: "1001↵    at makeError (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:23:17)↵    at module.exports (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/native/_error.js:31:13)↵    at SAFEApp.get connection [as connection] (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/app.js:189:13)↵    at MutableDataInterface.newEntries (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:640:42)↵    at MutableData.quickSetup (/opt/Maidsafe/Peruse/resources/app.asar/node_modules/@maidsafe/safe-node-app/src/api/mutable.js:283:33)↵    at app.mutableData.newRandomPublic.then.mData (http://localhost:5000/index.js:19682:83)↵    at <anonymous>"__proto__: Objectconstructor: function Error()message: ""name: "Error"toString: function toString()__proto__: Objectconstructor: function Object()hasOwnProperty: function hasOwnProperty()isPrototypeOf: function isPrototypeOf()propertyIsEnumerable: function propertyIsEnumerable()toLocaleString: function toLocaleString()toString: function toString()valueOf: function valueOf()arguments: nullcaller: nulllength: 0name: "valueOf"__proto__: function ()__defineGetter__: function __defineGetter__()arguments: nullcaller: nulllength: 2name: "__defineGetter__"__proto__: function ()__defineSetter__: function __defineSetter__()arguments: nullcaller: nulllength: 2name: "__defineSetter__"__proto__: function ()__lookupGetter__: function __lookupGetter__()__lookupSetter__: function __lookupSetter__()get __proto__: function __proto__()set __proto__: function __proto__()

#2

Hi @Folatt, the tutorial has been indeed updated to be compatible with @maidsafe/safe-app-nodejs v0.9.x (https://github.com/maidsafe/dev_website/pull/36) when that version was published.

It seems you are taking the code that the tutorial shows for developing a desktop app (https://hub.safedev.org/platform/nodejs/) but trying to run it as a web app within the browser.

If you are trying to create a web app you should follow this other tutorial: https://hub.safedev.org/platform/web/


#3

FYI I’m working on a library SafenetworkJs that provides a simplified version of the SAFE API which can be used in either desktop or Web apps.

It started as a Web library (eg used in my RemoteStorage.js demo apps and Solid/SAFE Plume), and is currently working in a desktop app (SAFE FUSE) although it is work in progress.

This means some of the code has not yet been ported to the 0.9 API, or is ported but not tested, while there is a lot of code which is ported and working.

So the auth code works and there are a set of classes for working with mutable data, in particular for querying the standard containers (not tested in Web API, but it should manly 'just work :wink:).

Porting is pretty simple, so any areas that haven’t been ported mainly just need minor changes and some testing.


#4

That must have been very recently.

[edit]

No wait, it’s just different from what I see in the API docs.
Why are the API docs examples so different from the tutorials?


#5

Yes, Aug 6, a month ago, and safe-node-app v0.9.0 was published on Jul 31.

Can you reference to which docs you are referring to please? the docs at http://docs.maidsafe.net/beaker-plugin-safe-app/ are the old DOM API which is still available in Beaker Browser v0.10.2, whilst the docs at http://docs.maidsafe.net/safe_app_nodejs/ correspond to v0.9.x, and the new DOM API in Peruse v0.6 has only a couple of differences to that safe_app_nodejs v0.9 API


#6

I get the feeling that the web tutorial hasn’t been updated while the nodejs has.


#7

If you follow the web tutorial and use Beaker Browser v0.10.2 you shouldn’t face any problems, the code you are using seems to be from the desktop app tutorial, so that’s your first issue.

Now, whenever we make Peruse the official browser and finally officially deprecate Beaker Browser (hopefully soon) we will definitely need to change the web tutorial as the DOM API is different. I agree though that perhaps the web tutorial now can be more explicit about beaker browser being the official and the browser to be used for the purpose, I’ll look into enhancing that.


#8

I’m using Peruse already.

I also encounter another error when trying to use @maidsafe/safe-node-app.
The require line alone is already giving me an error.

safenetwork.js

const safeNodeApp = require('@maidsafe/safe-node-app')
...

Peruse devtools error

bindings.js:158 Uncaught TypeError: exists is not a function
    at Function.getRoot (bindings.js:158)
    at bindings (bindings.js:60)
    at Object../node_modules/weak/lib/weak.js (weak.js:7)
    at __webpack_require__ (bootstrap 87a193eb7d582ce2634b:678)
    at fn (bootstrap 87a193eb7d582ce2634b:88)
    at Object.<anonymous> (helpers.js:14)
    at Object../node_modules/@maidsafe/safe-node-app/src/helpers.js (helpers.js:158)
    at __webpack_require__ (bootstrap 87a193eb7d582ce2634b:678)
    at fn (bootstrap 87a193eb7d582ce2634b:88)
    at Object../node_modules/@maidsafe/safe-node-app/src/app.js (app.js:15)

#9

When doing a web app you cannot require any package like that, and you don’t need to require anything for having the DOM API available, so that line has to go away if you are coding a webapp.

In Peruse, for authorising the application, try the following as your authoriseAndConnect function within the safenetwork.js file:

let safeApp;

async function authoriseAndConnect() {
  let appInfo = {
    name: 'Hello SAFE Network',
    id: 'net.maidsafe.tutorials.web-app',
    version: '0.2.0',
    vendor: 'MaidSafe.net Ltd.'
  };
  safeApp = await window.safe.initialiseApp(appInfo);
  console.log('Authorising SAFE application...');
  const authReqUri = await safeApp.auth.genAuthUri();
  const authUri = await window.safe.authorise(authReqUri);
  console.log('SAFE application authorised by user');
  await safeApp.auth.loginFromUri(authUri);
  console.log("Application connected to the network");
};

You should get the authorisation popup in Peruse and see that the app gets connected after you authorise it. I’ll try to get the complete safenetwork.js code for Peruse now for you, so please let me know how you get along with this.


#10

@Folatt , this is the complete code for the safenetwork.js file for the web app tutorial to work on Peruse v0.6, please note that I renamed the removeItems function to deleteItems:

let safeApp;

async function authoriseAndConnect() {
  let appInfo = {
    name: 'Hello SAFE Network',
    id: 'net.maidsafe.tutorials.web-app',
    version: '0.2.0',
    vendor: 'MaidSafe.net Ltd.'
  };
  safeApp = await window.safe.initialiseApp(appInfo);
  console.log('Authorising SAFE application...');
  const authReqUri = await safeApp.auth.genAuthUri();
  const authUri = await window.safe.authorise(authReqUri);
  console.log('SAFE application authorised by user');
  await safeApp.auth.loginFromUri(authUri);
  console.log("Application connected to the network");
};

let md;
async function createMutableData() {
  console.log("Creating MutableData with initial dataset...");
  const typeTag = 15000;
  md = await safeApp.mutableData.newRandomPublic(typeTag);
  const initialData = {
    "random_key_1": JSON.stringify({
        text: 'Scotland to try Scotch whisky',
        made: false
      }),
    "random_key_2": JSON.stringify({
        text: 'Patagonia before I\'m too old',
        made: false
      })
  };
  await md.quickSetup(initialData);
}

async function getItems() {
  const entries = await md.getEntries();
  let entriesList = await entries.listEntries();
  let items = [];
  entriesList.forEach((entry) => {
    const value = entry.value;
    if (value.buf.length == 0) return;
    const parsedValue = JSON.parse(value.buf);
    items.push({ key: entry.key, value: parsedValue, version: value.version });
  });
  return items;
};

async function insertItem(key, value) {
  const mutations = await safeApp.mutableData.newMutation();
  await mutations.insert(key, JSON.stringify(value));
  await md.applyEntriesMutation(mutations);
};

async function updateItem(key, value, version) {
  const mutations = await safeApp.mutableData.newMutation();
  await mutations.update(key, JSON.stringify(value), version + 1);
  await md.applyEntriesMutation(mutations);
};

async function deleteItems(items) {
  const mutations = await safeApp.mutableData.newMutation();
  items.forEach(async (item) => {
    await mutations.delete(item.key, item.version + 1);
  });
  await md.applyEntriesMutation(mutations);
};

module.exports = {
  authoriseAndConnect,
  createMutableData,
  getItems,
  insertItem,
  updateItem,
  deleteItems
};

#11

It does not seem to get past const authUri = await window.safe.authorise(authReqUri)
And I don’t know how to look at this in the console as the console sees await as an unexpected identifier.


#12

Hi @Folatt, I’m glad you keep plugging away at this. I often read your posts and would like to help, but find they lack information to help me understand what to suggest so it might help you get more responses to spell things out at each point.

So with regard to your last post, what steps are you taking?

Have you set anything in the environment, is Peruse running, have you logged into an account, does anything happen when you reach that authorise() statement?


#13

My steps are:

  1. yarn start
  2. run Peruse
  3. open localhost:5000

environment

  use: [
    ['@neutrinojs/vue', {
      html: {
        title: 'Webapp'
      }
    }],
    (neutrino) => {
      if (process.env.NODE_ENV === 'development') {
        neutrino.config.devtool('cheap-module-source-map');
      }
    }
  ]
};

Peruse is running.
I did not log into an account. Should I?
Nothing happens when I reach the authorise statement.


#14

Definitely worth a try. Peruse is still very early code unlike SAFE Browser and I know there are issues related to this so I always log in first atm.

Nothing happens when I reach the authorise statement.

I would expect there to be some output in the Browser console. I’m not sure what though because I’m working with a desktop app myself rather than Web.


#15

Will do after work.

Same here, but I got nothing.
In fact, now that you’ve mentioned it,
shouldn’t I expect the browser to show a red bar telling me that I should log in?


#16

Try this, which I PM’d you about last week (wrapping the code in an anonymous async method):


#17

In SAFE Browser but not yet in Peruse I think. So until then I log in first.


#18

Like this?

async function authoriseAndConnect() {
  
  (async () => {
    const info        = { id: '1', name: '2', vendor: '3' };
    const permissions = {};
    const options     = { own_container: true };

    const appHandle = await window.safeApp.initialise(info);
    const uri       = await window.safeApp.authorise(appHandle, permissions, options);
    await window.safeApp.connectAuthorised(appHandle, uri);

    const mdHandle = await window.safeApp.getOwnContainer(appHandle)
    const entriesHandle = await window.safeMutableData.getEntries(mdHandle)
    
   // etc.
   })()
}

That give me this result:

Uncaught (in promise) TypeError: Cannot read property 'initialise' of undefined

For some reason I do receive the red bar telling me to log in in Peruse today.
Logging in has not helped though.


#19

Sorry, the example I gave contains outdated code when using it within Peruse.

It’s not necessary to wrap the anonymous function into a named function. Here’s a correct snippet:

(async () => {
    const info  = { id: '1', name: '2', vendor: '3' };
    const perms = {};
    const opts  = { own_container: true };

    const app      = await safe.initialiseApp(info);
    const auth_uri = await app.auth.genAuthUri(perms, opts);
    const uri      = await safe.authorise(auth_uri);
    await app.auth.loginFromUri(uri);

    const cont = await app.auth.getOwnContainer();
    console.info(await cont.getNameAndTag());
})();

Just open the Peruse browser using --args --mock --preload. (I tested using the 0.7.0-rc.2 version.) Open a new tab, open the console in there, paste the snippet and press enter:

You’ll see the authorization window pop-up. The console.info will log the {name: ..., typeTag: ...} object in this case.


The console also autocompletes whenever you type something, use it to your advantage! Your result (‘Cannot read property … of undefined’) can be debugged quite easily this way. Type in window.safeApp and you see it’s indeed undefined.


#20

One of the --args --mock --preload arguments is what made it work for me.
Without it, it won’t authenticate.