GithubHelp home page GithubHelp logo

safe-api's People

Contributors

eblanshey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

dirvine artogn

safe-api's Issues

Error: No transports available

Hi, nice project.
I tried to run a little test script from the commandline:
´´´
var safe_api = require('safe-api')
safe_api.setup({ firebaseApp: 'intense-torch-2025' })
´´´
but i get an error somewhere:
´´´
Error: No transports available
at Error (native)
at Dh (C:\Users\henrud\Documents\SAFE\safe-api-test\node_modules\safe-api\lib\index.js:16920:645)
at Fh (C:\Users\henrud\Documents\SAFE\safe-api-test\node_modules\safe-api\lib\index.js:16921:23)
at new Eh (C:\Users\henrud\Documents\SAFE\safe-api-test\node_modules\safe-api\lib\index.js:16920:881)
at bi (C:\Users\henrud\Documents\SAFE\safe-api-test\node_modules\safe-api\lib\index.js:16940:230)
at null._onTimeout (C:\Users\henrud\Documents\SAFE\safe-api-test\node_modules\safe-api\lib\index.js:16938:147)
at Timer.listOnTimeout (timers.js:119:15)
´´´
Any ideas?
and btw how do i build your project so it doesnt minimize the code?

Proposal: introduce models to help maintain objects and interact with SAFE

Introduction

Allow devs to define all of the application's entities and collections using models. The following functionality should be provided by the models:

  • Easy network GETS, PUTS, and DELETES: Provide get(), put(), and delete() methods on the models. Only PUT if data has changed, and DELETE if data exists.
  • Custom Helper Methods: allow setting user-defined methods on the model that act as helpers. As a basic example, a fullName() helper method could concatenate an object's first_name and last_name fields and then return the result.
  • Data validation: every app has set rules on what its data should look like. If an object contains a name field, for example, it expects it to be a string of a certain length, and NOT an integer or array. In SAFE, anybody can upload any data they want, so client-side data validation must be included in the app to prevent breakage. Task: create an easy way for the dev to set rules for their data. If rules are not met, then either use some defaults, or remove the object entirely. The dev will have a guarantee that the object returned from the API will follow the application's rules, even if a nefarious user tries to cause trouble. Allow devs to attach error messages for each validation rule so they could be used to quickly validate forms, too. See proposal here.
  • Data versioning: Object schemas change over time: a string field may eventually become an array field to allow multiple options. The developer cannot go and change all existing data to reflect this change, so his app needs to be smart and maintain backwards compatibility for every single object schema change. Over time, this can lead to massive headaches and spaghetti code in order to make sure old data is supported. Task: introduce versioning for models. Every object PUT to the network should have an integer version number. Upon fetching it, if the model detects that the version number is not the latest one, it incrementally modifies its data using the model's set rules until it matches the rules of the latest version. This should make app development much easier knowing that even old data on the network will be returned using the latest schemas. This allows newer code to read older data, but not vice versa. Bonus: if the requester of the data is also the data owner, automatically UPDATE the object on the network (if possible) so that the same version processing won't be required in the future for all requests. With Structured Data this will be free. See proposal here.

Proposed Basic Functionality

The following illustrates the proposed functionality, not including validation and versioning

// Define a new model (using ES2015 syntax, submit issue if you want ES5)
class Post extends Model {
    // Define a helper getter method (optional)
    titleUpper() {
        return this.data('title').toUpperCase();
    }
}
Post.name = 'Post';

// Let's create a new model
var data = {
    title: "My New Post",
    description: "This is my post description"
};

// Use constructor to fill model
var post = new Post(data);

// Read and set data from the model.
// For consistency, all top-level data must be objects.
post.raw(); // returns the data as is
post.data('title'); // "My New Post"
post.set('description', 'This is a new description'); // returns true
post.data('description'); // 'This is a new description'
post.set('myArray', ['a', 'b']); // true
post.get('myArray.0'); // 'a'

// Use our custom method
post.titleUpper(); // "MY NEW POST"

// Set multiple fields at once
post.fill({title: 'New Title', description: 'A better description'}); // true

// Does the data exist on the network?
post.exists; // bool

// Model name -- could be used as part of the tag_type in Structured Data (SD)
post.name; // 'Post'

// Save the data to the network. Throw error if not authenticated,
// or object does not belong to authenticated user.
post.put()
    .then(() => console.log('The model has been saved.'))
    .catch((error) => console.log(error));

// Userid of the owner. `put()` sets the owner to our userid, of course.
post.owner; // 'aUserIdHere', or null if not exists yet

// ID of this model -- could be the `identifier` in SD
post.id; // 'aPostId', or null if not exists yet

// get a model from the network using its userid and id
var post = Post.identify(userid, id);
post.get()
    .then((post) => console.log(post.exists ? 'Found it' : 'Not found'))
    .catch((error) => console.log(error));
post.loading; // bool. is it currently doing a GET, PUT, or DELETE request?
post.networkError; // string if network error encountered.

// Date last retrieved or put to the network
post.timestamp; // Date() instance, or null if not yet fetched/uploaded

// Delete the model. throws error if you're not the owner.
post.delete();
post.exists; // false

Thoughts, suggestions, and questions welcome!

Proposal: add validation and versioning to models

Introduction

Add data validation and data versioning to models, which is absolutely critical for the SAFE network. The descriptions below are a copy-paste from the proposal:

  • Data validation: every app has set rules on what its data should look like. If an object contains a name field, for example, it expects it to be a string of a certain length, and NOT an integer or array. In SAFE, anybody can upload any data they want, so client-side data validation must be included in the app to prevent breakage. Task: create an easy way for the dev to set rules for their data. If rules are not met, then either use some defaults, or remove the object entirely. The dev will have a guarantee that the object returned from the API will follow the application's rules, even if a nefarious user tries to cause trouble. Allow devs to attach error messages for each validation rule so they could be used to quickly validate forms, too.
  • Data versioning: Object schemas change over time: a string field may eventually become an array field to allow multiple options. The developer cannot go and change all existing data to reflect this change, so his app needs to be smart and maintain backwards compatibility for every single object schema change. Over time, this can lead to massive headaches and spaghetti code in order to make sure old data is supported. Task: introduce versioning for models. Every object PUT to the network should have an integer version number. Upon fetching it, if the model detects that the version number is not the latest one, it incrementally modifies its data using the model's set rules until it matches the rules of the latest version. This should make app development much easier knowing that even old data on the network will be returned using the latest schemas. This allows newer code to read older data, but not vice versa. Bonus: if the requester of the data is also the data owner, automatically UPDATE the object on the network (if possible) so that the same version processing won't be required in the future for all requests. With Structured Data this will be free.

Validation

Fields validated will have rules set like this:

// Define a model that has 5 properties: title, myNumber, date, myName, and hello.
Model.validation = {
    0: { // version of the rules (explained later)
        title: { // property name
            rules: Rules.required().string().max(100), // chainable rules
            default: "Default Title" // if fails validation, return this value
        },
        myNumber: {
            rules: Rules.integer().min(0),
            default: undefined // remove this property if validation fails
        },
        date: {
            rules: Rules.date().dateBetween('2015-01-01', '2015-12-31'), // as parsable by MomentJS
            default: Remove // special object for critical props that, if provided as default value, essentially ignores this whole object
        },
        myName: {
            rules: Rules.custom(function(entity) {
                if (!entity[myName].includes('Hi, my name is')) {
                    return false; // fails
                } else {
                    return true; // passes
                }
            default: 'Hi, my name is John Doe'
        },
        hello: {} // no validation at all, but this is required if the field is present
    }
}

Each property in the object being retrieved or put to the network must pass the validation rules provided. Each property is an object that contains rules and default, both optional.

A Rules object is supplied to rules that contains many chainable validation methods, including custom() allowing you to supply your own validation function that is passed an entity object. It should return a boolean indicating passing or failure. If it returns a function, then the return value of that function will be used as the new value. No validation rules are run if rules is not supplied. Rules are run in order of appearance.

If data is being PUT to the network and validation fails, it will throw an error. The required() rule only applies before PUTs.

If data is being retrieved, the default value will be used in place of fields that failed validation. If default is not set, it defaults to undefined. If default is undefined, the field will be removed from the entity. If default is a function, it will be called given the entity object, allowing to create a default value based on other fields. If default is an instance of "Remove", the entire entity will be set as exists = false and data discarded, in other words, as if it doesn't exist on the network. If the field does not exist on the object at all, it will be set to the default value.

Example validation rules:

  • Primitives: string, number, array, boolean, etc
  • Numbers: max, min, between
  • Strings: max, min, size, alpha, alphanum, base64, etc
  • Date: date, dateBetween, dateFormat
  • Field modifiers: trim (strings)

If a field is present in the entity that has no matching key in the validation object, it is discarded. Thus, every field in an entity must be defined in the validator.

Versioning

When defining models, add a static currentVersion property that for the first time should be 0. Every time any of the above rules are modified in a non-backwards-compatible way, such as a string becoming an array, increment the static version by one and add a transformer function that transforms the entity object from the previous rule-set.

Every entity must have a property version set that is an integer value, indicating the version of the validation used when creating or last updating the object. If not set on the entity, it is discarded (discuss--or should it assume the latest version?)

When changing an existing rule, the process is as follows:

  1. Increment the model version by 1.
  2. Add a new validation object with the key set as the new model version
  3. Add a transformer that takes an entity using the previous ruleset and returns an updated entity.

Take the following Post model as an example:

class Post extends Model {}
Post.name = 'Post'
Post.currentVersion = 0
Post.validation = {
    0: {
        title: {
            rules: Rules.required().string()
        },
        category: {
            rules: Rules.required().string() // e.g. 'art'
        }
    }
}   

We now have Post model whose current version is 0. The current validation rules define title and category fields, which are both strings. Now what if we wanted to make category an array instead of a string to support multiple categories? We can't just change the rules, since all the existing data on the network has a string, and thus would fail validation. We need to define a transformer along with the new rule:

Post.currentVersion = 1 // increment the current model version
Post.validation = {
    0: {
        title: {
            rules: Rules.string()
        },
        category: {
            rules: Rules.string(), // e.g. 'art'
            default: 'Misc'
        }
    },
    1: { // add a new validation rule that overrides the previous one
        category: {
            rules: Rules.array(),
            default: ['Misc']
        }
    }   
}
Post.transformers = {
    // Takes entity with version 0 and turns it into version 1.
    // By the time this function is called, validation 0 has already been called.
    // Thus you are guaranteed to work with valid data.
    1: function(entity) {
        // turn category into an array
        entity.category = [entity.category]
    }

If a post is retrieved from the network with a version set to 0, meaning it was uploaded when version 1 didn't exist, the following will happen:

  1. Run entity through validation for version 0.
  2. Transform the resulting entity to version 1.
  3. Run validation for version 1
  4. If the entity owner is the currently authenticated user, PUT the latest object data to the network to prevent running transformers again.

Future Considerations

  • Since the dev already spent time creating validation rules, it makes sense to allow him to use them to validate user input from a form. How should this be taken care of? As an example, every field in validation could have a string "inputError" that contains an error if validation didn't pass, and an addition rules object could be provided that only ran on input, that could have required(), isNumeric(), etc. I am thinking to delay this part as it's not technically related to SAFE.

Have any comments or questions? Please share!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.