Aklımda Kalası Kelimeler

* давайте работать вместе
* Zarf ve Mazruf, Zerafet(xHoyratlık) ile aynı kökten(za-ra-fe) gelir
* Bedesten
* Suç subuta ermiştir - Suç sabit olmuştur

5 Ekim 2015 Pazartesi

json-schema-defaults ile node.js üstünde .json uzantılı şemaları dinamik yüklemek

Tüm kodunu en aşağıya yapıştırdım ama ekelediğim yeri biraz anlatmış olayım:
var getRemoteRef = function (_uri, definitions) {
// loadschema ileride async olarak şema yüklemelerini sağlamak için kullanacağım için şimdiden hazır edeyim dedim.
    var loadSchema = function (uri, callback) {
        var request = require('request');
        request(uri, function (err, res, body) {
            console.log(body); // Bu gelen şemayı doğrulamaya göndermeden önce JSON'a çevirmelisiniz.

            if (err || res.statusCode >= 400)
                callback(err || new Error('Loading error: ' + res.statusCode));
            else {
                callback(null, JSON.parse(body));
            }
        });
    };

    // node uygulamasının hangi fiziksel klasörde olduğunu buluyorum
    var appRoot = require('app-root-path'),
        // dosya yolunun schema'lı halini oluşturuyorum
        indexOfSchema = _uri.indexOf('/schema/');

    // .json dosyasının tam yolunu bulduktan sonra 
    var filePath = appRoot + "/node/" + _uri.substr(indexOfSchema);
    console.log("Path: " + filePath);

    // reuqire ile çekiyoruz
    var result = require(filePath);

    if (!isObject(result)) {
        return result;
    }
    return cloneJSON(result);
}
(function (root, factory) {

    'use strict';

    if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
        // CommonJS
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        // AMD
        define('json-schema-defaults', [], function () {
            return factory();
        });
    } else {
        // global with noConflict
        var jsonSchemaDefaults = root.jsonSchemaDefaults;
        root.jsonSchemaDefaults = factory();
        root.jsonSchemaDefaults.noConflict = function () {
            var defaults = root.jsonSchemaDefaults;
            root.jsonSchemaDefaults = jsonSchemaDefaults;
            return defaults;
        };
    }

}(this, function () {

    'use strict';

    var ajv = null;

    /**
     * check whether item is plain object
     * @param {*} item
     * @return {Boolean}
     */
    var isObject = function (item) {
        return typeof item === 'object' && item !== null && item.toString() === {}.toString();
    };

    /**
     * deep JSON object clone
     *
     * @param {Object} source
     * @return {Object}
     */
    var cloneJSON = function (source) {
        return JSON.parse(JSON.stringify(source));
    };

    /**
     * returns a result of deep merge of two objects
     *
     * @param {Object} target
     * @param {Object} source
     * @return {Object}
     */
    var merge = function (target, source) {
        target = cloneJSON(target);

        for (var key in source) {
            if (source.hasOwnProperty(key)) {
                if (isObject(target[key]) && isObject(source[key])) {
                    target[key] = merge(target[key], source[key]);
                } else {
                    target[key] = source[key];
                }
            }
        }
        return target;
    };

    /**
     * merge list of objects from allOf properties
     * if some of objects contains $ref field extracts this reference and merge it
     *
     * @param {Array} allOfList
     * @param {Object} definitions
     * @return {Object}
     */
    var mergeAllOf = function (allOfList, definitions) {
        var length = allOfList.length,
            index = -1,
            result = {};

        while (++index < length) {
            var item = allOfList[index];

            item = (typeof item.$ref !== 'undefined') ? getLocalRef(item.$ref, definitions) : item;

            result = merge(result, item);
        }

        return result;
    };

    /**
     * get object by reference. works only with local references that points on
     * definitions object
     *
     * @param {String} path
     * @param {Object} definitions
     * @return {Object}
     */
    var getLocalRef = function (path, definitions) {
        path = path.replace(/^#\/definitions\//, '').split('/');

        var find = function (path, root) {
            var key = path.shift();
            if (!root[key]) {
                return {};
            } else if (!path.length) {
                return root[key];
            } else {
                return find(path, root[key]);
            }
        };

        var result = find(path, definitions);

        if (!isObject(result)) {
            return result;
        }
        return cloneJSON(result);
    };


    var getRemoteRef = function (_uri, definitions) {
        var loadSchema = function (uri, callback) {
            var request = require('request');
            request(uri, function (err, res, body) {
                console.log(body); // Bu gelen şemayı doğrulamaya göndermeden önce JSON'a çevirmelisiniz.

                if (err || res.statusCode >= 400)
                    callback(err || new Error('Loading error: ' + res.statusCode));
                else {
                    callback(null, JSON.parse(body));
                }
            });
        };

        var Path = require('path'),
            appRoot = require('app-root-path'),
            indexOfSchema = _uri.indexOf('/schema/');

        var filePath = appRoot + "/node/" + _uri.substr(indexOfSchema);
        console.log("Path: " + filePath);
        var result = require(filePath);
        if (!isObject(result)) {
            return result;
        }
        return cloneJSON(result);
    }


    /**
     * returns a object that built with default values from json schema
     *
     * @param {Object} schema
     * @param {Object} definitions
     * @return {Object}
     */
    var defaults = function (schema, definitions) {
        console.log("schema: " + JSON.stringify(schema, null, ' '));
        console.log("definitions: " + JSON.stringify(definitions));
        if (typeof schema['default'] !== 'undefined') {

            return schema['default'];

        }
        else if (typeof schema.allOf !== 'undefined') {

            var mergedItem = mergeAllOf(schema.allOf, definitions);
            console.log("MergedItem: " + JSON.stringify(mergedItem));
            return defaults(mergedItem, definitions);

        }
        else if (typeof schema.$ref !== 'undefined') {
            console.log("Reference name: " + schema.$ref);
            var reference;
            if (schema.$ref.indexOf("http") == 0) {
                reference = getRemoteRef(schema.$ref, definitions);
                //reference= getRemoteRef("http://localhost:3000/schema/providers/login/test.json", definitions);
            } else {
                reference = getLocalRef(schema.$ref, definitions);
            }

            console.log("Refernce: " + JSON.stringify(reference));
            return defaults(reference, definitions);

        }
        else if (schema.type === 'object') {

            if (!schema.properties) {
                return {};
            }

            for (var key in schema.properties) {
                if (schema.properties.hasOwnProperty(key)) {
                    schema.properties[key] = defaults(schema.properties[key], definitions);

                    if (typeof schema.properties[key] === 'undefined') {
                        delete schema.properties[key];
                    }
                }
            }

            return schema.properties;

        }
        else if (schema.type === 'array') {

            if (!schema.items) {
                return [];
            }
            return [defaults(schema.items, definitions)];

        }
        else if (schema.type && typeof(schema.type) === 'object') {

            console.log("Reference name: " + schema.type.$ref);
            var reference = getLocalRef(schema.$ref, definitions);
            console.log("Refernce: " + JSON.stringify(reference));
            return defaults(reference, definitions);

            if (!schema.items) {
                return [];
            }
            return [defaults(schema.items, definitions)];

        }

    };

    /**
     * main function
     *
     * @param {Object} schema
     * @param {Object|undefined} definitions
     * @return {Object}
     */
    return function (schema, definitions, _ajv) {
        ajv = _ajv;

        if (definitions && Array.isArray(definitions)) {
            var defs = {};
            definitions.forEach(function (_definition) {
                defs = merge(_definition, schema.definitions);
            });
            definitions = defs;
        }

        if (typeof definitions === 'undefined') {
            definitions = schema.definitions || {};
        }
        else if (isObject(schema.definitions)) {
            definitions = merge(definitions, schema.definitions);
        }

        return defaults(cloneJSON(schema), definitions);
    };

}));

AJV'yi JSON doğrulamalarında kullanmak




Node.js üstünde AJV'de harici referansları olan ".json" uzantılı şemaların yüklenmesini birazcık anlatayım. Kodları test etmeden yazıyorum asıl olan temelde neyin döndüğüdür.

Ana şemamız index.json ve içinde harici referans olarak test.json'ı içersin (bkz. definitons -> "TW": {"$ref": "http://localhost:3000/schema/providers/login/test.json"}
{
  "id": "http://localhost:3000/schema/providers/login/index",
  "title": "Providers",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "description": "Kullanıcı Giriş Sağlayıcıları",
  "type": "object",
  "definitions": {
    "TW": {"$ref": "http://localhost:3000/schema/providers/login/test.json"}
  },
  "additionalProperties": false,
  "properties": {
    "TW": {"$ref": "#/definitions/TW"}
  }
}
test.json
{
  "id": "http://localhost:3000/schema/providers/login/test.json",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "default": 0
    },
    "id_str": {"type": "string"},
    "name": {
      "type": "string",
      "default": ""
    }
  },
  "required": [
    "id",
    "name"
  ]
}
Her iki şemanın ID'sinin, URL adresleri olduğuna dikkat! Çünkü bu sayede hem eşsizliği sağlamış olursunuz hem de bağlantılarını kopartmamış olursunuz. Ayrıca $ref kısmını definitions içinde veriyor ve properties içindeki özelliklere buradan verdiğimiz için aynı tipi başka bir yerde kullansakta bir kez http talebi yapmış olacağız (Örn. properties:{ ev_adresi:{$ref:'http:/..../adres.json'}, is_adresi:{$ref:'http:/..../adres.json'}, ..} olsaydı aynı json 2 kez çağrılmış olacaktı).

addSchema metodu

2 Parametre alıyor; ilki şemanın kendisi(json nesnesi olacak), ikincisi ise eğer şemanın içinde "id" tanımlanmamışsa kullanılması için sizin belirteceğiniz "id" değeri(string olacak).
var Ajv = require('ajv'),
    ajv = Ajv({removeAdditional:true});

function addSchema() {

    var schTest = require('./providers/login/test.json');
    var schIndex = require('./providers/login/index.json');
    ajv.addSchema(schTest);
    ajv.addSchema(schIndex);
    var bValid = ajv.validate(schIndex.id, {TW: {id: 2, a: 1, name: "cem"}});
    if (!bValid) {
        console.log("Hatalıysam ara: ");
        console.log(ajv.errors);
    }else{
        console.log("Herkül müsün kardeşim:");
        var sch2 = ajv.getSchema("http://localhost:3000/schema/providers/login/index");
        console.log(sch2.schema);
    }
}
addSchema();

compile metodu

var Ajv = require('ajv'),
    ajv = Ajv({removeAdditional:true});

function compile() {

    var schTest = require('./providers/login/test.json');
    var schIndex = require('./providers/login/index.json');
    ajv.addSchema(schTest);  // önce test şemasını ekleyelimki index.json içinde kullanılmadan önce hazır olsun
    var validate = ajv.compile(schIndex); // index.json'ı derleyerek doğrulayacak fonksiyonumuza ulaşalım.
    console.log(validate.schema) // şemamızın içinde test.json'ı referans olarak görürüz ama doğrulama sırasında bu referanslara bakarak veri nesnesini doğrulayacaktır
    var a = validate({TW: {id: 2, name: "cem"}})  // hata vermez ve başarıyla doğrulanmış olur

    if(validate.errors){
      console.log(validate.errors)
    }else{
      console.log(a);
    }
}
compile();

compileAsync metodu

Bu fonksiyonu kullanmadan önce asenkron olarak yükleme işini yapacak fonksiyonu tanımlamalısınız ve yükleme tamamlandıktan sonra doğrulama yapacak callback fonksiyonunu girmelisiniz. Bunun için AJV'nin options larından loadSchema'yı kullanacağız.

ajv = Ajv({
    loadSchema: function (uri, callback) {
        var request = require('request');
        request(uri, function (err, res, body) {
            console.log(body); // Bu gelen şemayı doğrulamaya göndermeden önce JSON'a çevirmelisiniz.

            if (err || res.statusCode >= 400)
                callback(err || new Error('Loading error: ' + res.statusCode));
            else {
                callback(null, JSON.parse(body));
            }
        });
    }
});

function compileAsync() {
    var sch = require('./providers/login/index.json');
    console.log("sch--------------");
    console.log(JSON.stringify(sch));

    ajv.compileAsync(sch, function (err, validate) {
        console.log(validate); // görelim ne var elimizde

        if (err) return; // hata varsa dön bebeğim

        var a = validate({TW:{id: 2, name: "cem"}});
        console.log(validate.errors); // null döner
        console.log(a);  // true döner(hatasız);
    });
}
compileAsync();
Tüm kod:
var Ajv = require('ajv'),
//sch = require('./index'),
    ajv = Ajv({removeAdditional: true}); // options can be passed
ajv = Ajv({
    loadSchema: function (uri, callback) {
        var request = require('request');
        request(uri, function (err, res, body) {
            console.log("request ended");
            console.log(body);

            if (err || res.statusCode >= 400)
                callback(err || new Error('Loading error: ' + res.statusCode));
            else {
                callback(null, JSON.parse(body));
            }
        });
    }
});


function compile() {

    var schTest = require('./providers/login/test.json');
    var schIndex = require('./providers/login/index.json');
    ajv.addSchema(schTest);
    var validate = ajv.compile(schIndex);
    console.log(validate.schema);
    var a = validate({TW: {id: 2, name: "cem"}});
    console.log(validate.errors);
    console.log(a);
}
//compile();

function compileAsync() {
    var sch = require('./providers/login/index.json');
    console.log("sch--------------");
    console.log(JSON.stringify(sch));

    ajv.compileAsync(sch, function (err, validate) {
        console.log(validate);
        console.log("neredeyim")
        console.log("err: " + err);
        if (err) return;

        var a = validate({id: 2, name: "cem"})
        console.log(validate.errors)
        console.log(a);

        console.log("buradayım");
    });
}
//compileAsync();

function addSchema() {

    var schTest = require('./providers/login/test.json');
    var schIndex = require('./providers/login/index.json');
    ajv.addSchema(schTest);
    ajv.addSchema(schIndex);
    var bValid = ajv.validate(schIndex.id, {a: 1,TW: {id: 2, name: "cem"}});
    if (!bValid) {
        console.log("Hatalıysam ara: ");
        console.log(ajv.errors);
    }else{
        console.log("Herkül müsün kardeşim:");
        var sch2 = ajv.getSchema("http://localhost:3000/schema/providers/login/index");
        console.log(sch2.schema);
    }
}
//addSchema();