withBreak blocks

kai zhu kaizhu256 at gmail.com
Sun Feb 18 02:55:34 UTC 2018


> On Feb 18, 2018, at 4:52 AM, sagiv ben giat <sagiv.bengiat at gmail.com> wrote:
> 
> > Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?


@sagiv, if you need guidance by use-case/example, here’s a real-world example [1] of a validator “god” function (with 100% code-coverage [2]) that encapsulates most of the logic for validating user-inputs against the full swagger/openapi 2.0 spec [3].  attached screenshot showing how its used from browser-console (works just as well in nodejs).

and yes, the code-sample makes use of break statements in:
1. a recursive while loop to dereference schema pointers [4]
2. in switch/case blocks which are conceptually similar to what you want to do

[1] https://github.com/kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076 <https://github.com/kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076>
[2] https://kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html <https://kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html>
[3] https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md <https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md>
[4] https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject <https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject>


```javascript
/*
 * real-world example of swagger-validator from
 * https://github.com/kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076
 */

/*jslint
    bitwise: true,
    browser: true,
    maxerr: 8,
    maxlen: 100,
    node: true,
    nomen: true,
    regexp: true,
    stupid: true
*/

local.validateBySwaggerSchema = function (options) {
/*
 * this function will validate options.data against the swagger options.schema
 * according to the spec defined at:
 * http://json-schema.org/draft-04/json-schema-validation.html#rfc.section.5
 */
    var $ref,
        circularList,
        data,
        dataReadonlyRemove2,
        ii,
        oneOf,
        schema,
        test,
        tmp;
    if (!options.schema) {
        return;
    }
    data = options.data;
    options.dataReadonlyRemove = options.dataReadonlyRemove || [{}, '', null];
    dataReadonlyRemove2 = options.dataReadonlyRemove[2] || {};
    schema = options.schema;
    circularList = [];
    while (true) {
        // dereference schema.schema
        while (schema.schema) {
            schema = schema.schema;
        }
        // dereference schema.oneOf
        oneOf = (data && schema.oneOf) || [];
        for (ii = 0; ii < oneOf.length; ii += 1) {
            tmp = String(oneOf[ii] && oneOf[ii].$ref)
                .replace('http://json-schema.org/draft-04/schema#', '#');
            switch (tmp + ' ' + (!local.isNullOrUndefined(data.$ref) || data.in)) {
            case '#/definitions/bodyParameter body':
            case '#/definitions/formDataParameterSubSchema formData':
            case '#/definitions/headerParameterSubSchema header':
            case '#/definitions/jsonReference true':
            case '#/definitions/pathParameterSubSchema path':
            case '#/definitions/queryParameterSubSchema query':
                schema = local.swaggerSchemaJson.definitions[tmp.split('/')[2]];
                break;
            default:
                switch (tmp) {
                case '#/definitions/bodyParameter':
                case '#/definitions/jsonReference':
                    schema = oneOf[ii ^ 1];
                    break;
                }
            }
            if (!schema.oneOf) {
                break;
            }
        }
        // dereference schema.$ref
        $ref = schema && schema.$ref;
        if (!$ref) {
            break;
        }
        test = circularList.indexOf($ref) < 0;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'schemaDeferenceCircular',
            prefix: options.prefix,
            schema: schema
        });
        circularList.push($ref);
        tmp = $ref.split('/').slice(-2);
        schema = $ref.indexOf('http://json-schema.org/draft-04/schema#/') === 0
            ? local.swaggerSchemaJson[tmp[0]]
            : options.swaggerJson[tmp[0]];
        schema = schema && schema[tmp[1]];
        test = schema;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'schemaDeference',
            prefix: options.prefix,
            schema: options.schema
        });
    }
    if (options.modeDereference) {
        if (options.modeDereferenceDepth > 1) {
            schema = local.jsonCopy(schema);
            Object.keys(schema.properties || {}).forEach(function (key) {
                schema.properties[key] = local.validateBySwaggerSchema({
                    // dereference property
                    modeDereference: true,
                    modeDereferenceDepth: options.modeDereferenceDepth - 1,
                    prefix: options.prefix.concat(['properties', key]),
                    schema: schema.properties[key],
                    swaggerJson: options.swaggerJson
                });
            });
        }
        return schema;
    }
    // validate schema.default
    if (options.modeDefault) {
        data = schema.default;
    }
    // validate semanticRequired
    test = options.modeDefault ||
        !local.isNullOrUndefined(data) ||
        schema.required !== true ||
        schema['x-swgg-notRequired'];
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'semanticRequired',
        prefix: options.prefix,
        schema: schema
    });
    if (local.isNullOrUndefined(data)) {
        return;
    }
    // validate semanticRequiredArrayItems
    test = !options.modeSchema || local.schemaPType(data) !== 'array' ||
        (typeof local.schemaPItems(data) === 'object' && local.schemaPItems(data));
    local.throwSwaggerError(!test && {
        errorType: 'semanticRequiredArrayItems',
        prefix: options.prefix,
        schema: data
    });
    // remove readOnly property
    if (schema.readOnly) {
        delete options.dataReadonlyRemove[0][options.dataReadonlyRemove[1]];
    }
    // optimization - validate schema.type first
    // 5.5.2. type
    // https://swagger.io/docs/specification/data-models/data-types/
    // https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#data-types
    switch (local.schemaPType(schema)) {
    case 'array':
        test = Array.isArray(data);
        break;
    case 'boolean':
        test = typeof data === 'boolean';
        break;
    case 'file':
        test = !options.modeSchema;
        break;
    case 'integer':
        test = Number.isFinite(data) && Math.floor(data) === data;
        switch (schema.format) {
        case 'int32':
            break;
        case 'int64':
            break;
        }
        break;
    case 'number':
        test = Number.isFinite(data);
        switch (schema.format) {
        case 'double':
            break;
        case 'float':
            break;
        }
        break;
    case 'string':
        test = typeof data === 'string' ||
            (!options.modeSchema && schema.format === 'binary');
        switch (test && !options.modeSchema && schema.format) {
        // Clarify 'byte' format #50
        // https://github.com/swagger-api/swagger-spec/issues/50
        case 'byte':
            test = !(/[^\n\r\+\/0-9\=A-Za-z]/).test(data);
            break;
        case 'date':
        case 'date-time':
            test = JSON.stringify(new Date(data)) !== 'null';
            break;
        case 'email':
            test = local.regexpEmailValidate.test(data);
            break;
        case 'json':
            test = local.tryCatchOnError(function () {
                JSON.parse(data);
                return true;
            }, local.nop);
            break;
        case 'phone':
            test = local.regexpPhoneValidate.test(data);
            break;
        }
        break;
    default:
        test = options.modeSchema || typeof data === 'object';
        break;
    }
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemType',
        prefix: options.prefix,
        schema: schema,
        typeof: typeof data
    });
    tmp = typeof data;
    if (tmp === 'object' && Array.isArray(data)) {
        tmp = 'array';
    }
    switch (tmp) {
    // 5.1. Validation keywords for numeric instances (number and integer)
    case 'number':
        // 5.1.1. multipleOf
        test = typeof schema.multipleOf !== 'number' || data % schema.multipleOf === 0;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'numberMultipleOf',
            prefix: options.prefix,
            schema: schema
        });
        // 5.1.2. maximum and exclusiveMaximum
        test = typeof schema.maximum !== 'number' || (schema.exclusiveMaximum
            ? data < schema.maximum
            : data <= schema.maximum);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: schema.exclusiveMaximum
                ? 'numberExclusiveMaximum'
                : 'numberMaximum',
            prefix: options.prefix,
            schema: schema
        });
        // 5.1.3. minimum and exclusiveMinimum
        test = typeof schema.minimum !== 'number' || (schema.exclusiveMinimum
            ? data > schema.minimum
            : data >= schema.minimum);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: schema.exclusiveMinimum
                ? 'numberExclusiveMinimum'
                : 'numberMinimum',
            prefix: options.prefix,
            schema: schema
        });
        break;
    // 5.2. Validation keywords for strings
    case 'string':
        // 5.2.1. maxLength
        test = typeof schema.maxLength !== 'number' || data.length <= schema.maxLength;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringMaxLength',
            prefix: options.prefix,
            schema: schema
        });
        // 5.2.2. minLength
        test = typeof schema.minLength !== 'number' || data.length >= schema.minLength;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringMinLength',
            prefix: options.prefix,
            schema: schema
        });
        // 5.2.3. pattern
        test = !schema.pattern || new RegExp(schema.pattern).test(data);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringPattern',
            prefix: options.prefix,
            schema: schema
        });
        break;
    // 5.3. Validation keywords for arrays
    case 'array':
        // 5.3.1. additionalItems and items
        // swagger disallows array items
        data.forEach(function (element, ii) {
            // recurse - schema.additionalItems and schema.items
            local.validateBySwaggerSchema({
                data: element,
                dataReadonlyRemove: [dataReadonlyRemove2, ii, dataReadonlyRemove2[ii]],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([ii]),
                schema: local.schemaPItems(schema) || schema.additionalItems,
                swaggerJson: options.swaggerJson
            });
        });
        // 5.3.2. maxItems
        test = typeof schema.maxItems !== 'number' || data.length <= schema.maxItems;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayMaxItems',
            prefix: options.prefix,
            schema: schema
        });
        // 5.3.3. minItems
        test = typeof schema.minItems !== 'number' || data.length >= schema.minItems;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayMinItems',
            prefix: options.prefix,
            schema: schema
        });
        // 5.3.4. uniqueItems
        test = !schema.uniqueItems || data.every(function (element) {
            tmp = element;
            return data.indexOf(element) === data.lastIndexOf(element);
        });
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayUniqueItems',
            prefix: options.prefix,
            schema: schema,
            tmp: tmp
        });
        break;
    // 5.4. Validation keywords for objects
    case 'object':
        // 5.4.1. maxProperties
        test = typeof schema.maxProperties !== 'number' ||
            Object.keys(data).length <= schema.maxProperties;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'objectMaxProperties',
            prefix: options.prefix,
            schema: schema
        });
        // 5.4.2. minProperties
        test = typeof schema.minProperties !== 'number' ||
            Object.keys(data).length >= schema.minProperties;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'objectMinProperties',
            prefix: options.prefix,
            schema: schema
        });
        // 5.4.3. required
        local.normalizeValue('list', schema.required).forEach(function (key) {
            test = !local.isNullOrUndefined(data[key]);
            local.throwSwaggerError(!test && {
                data: data,
                errorType: 'objectRequired',
                key: key,
                prefix: options.prefix,
                schema: schema
            });
        });
        // 5.4.4. additionalProperties, properties and patternProperties
        Object.keys(data).forEach(function (key) {
            tmp = null;
            if (schema.properties && schema.properties[key]) {
                tmp = true;
                // recurse - schema.properties
                local.validateBySwaggerSchema({
                    data: data[key],
                    dataReadonlyRemove: [
                        dataReadonlyRemove2,
                        key,
                        dataReadonlyRemove2[key]
                    ],
                    modeSchema: options.modeSchema,
                    prefix: options.prefix.concat([key]),
                    schema: schema.properties[key],
                    swaggerJson: options.swaggerJson
                });
            }
            Object.keys(schema.patternProperties || {}).forEach(function (rgx) {
                if (new RegExp(rgx).test(key)) {
                    tmp = true;
                    // recurse - schema.patternProperties
                    local.validateBySwaggerSchema({
                        data: data[key],
                        modeSchema: options.modeSchema,
                        prefix: options.prefix.concat([key]),
                        schema: schema.patternProperties[rgx],
                        swaggerJson: options.swaggerJson
                    });
                }
            });
/*
* validate
* 5.4.4.4. If "additionalProperties" has boolean value false
*
* In this case, validation of the instance depends on the property set of
* "properties" and "patternProperties". In this section, the property names of
* "patternProperties" will be called regexes for convenience.
*
* The first step is to collect the following sets:
*
* s
* The property set of the instance to validate.
* p
* The property set from "properties".
* pp
* The property set from "patternProperties".
* Having collected these three sets, the process is as follows:
*
* remove from "s" all elements of "p", if any;
* for each regex in "pp", remove all elements of "s" which this regex matches.
* Validation of the instance succeeds if, after these two steps, set "s" is empty.
*/
            test = tmp || schema.additionalProperties !== false;
            local.throwSwaggerError(!test && {
                data: data,
                errorType: 'objectAdditionalProperties',
                key: key,
                prefix: options.prefix,
                schema: schema
            });
            // recurse - schema.additionalProperties
            local.validateBySwaggerSchema({
                data: data[key],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([key]),
                schema: schema.additionalProperties,
                swaggerJson: options.swaggerJson
            });
        });
        // 5.4.5. dependencies
        Object.keys(schema.dependencies || {}).forEach(function (key) {
            if (local.isNullOrUndefined(data[key])) {
                return;
            }
            // 5.4.5.2.1. Schema dependencies
            // recurse - schema.dependencies
            local.validateBySwaggerSchema({
                data: data[key],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([key]),
                schema: schema.dependencies[key],
                swaggerJson: options.swaggerJson
            });
            // 5.4.5.2.2. Property dependencies
            local.normalizeValue('list', schema.dependencies[key]).every(function (key2) {
                test = !local.isNullOrUndefined(data[key2]);
                local.throwSwaggerError(!test && {
                    data: data,
                    errorType: 'objectDependencies',
                    key: key,
                    key2: key2,
                    prefix: options.prefix,
                    schema: schema
                });
            });
        });
        break;
    }
    // 5.5. Validation keywords for any instance type
    // 5.5.1. enum
    tmp = schema.enum || (!options.modeSchema && (local.schemaPItems(schema) || {}).enum);
    test = !tmp || (Array.isArray(data)
        ? data
        : [data]).every(function (element) {
        return tmp.indexOf(element) >= 0;
    });
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemEnum',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.2. type
    local.nop();
    // 5.5.3. allOf
    (schema.allOf || []).forEach(function (element) {
        // recurse - schema.allOf
        local.validateBySwaggerSchema({
            data: data,
            prefix: options.prefix,
            modeSchema: options.modeSchema,
            schema: element,
            swaggerJson: options.swaggerJson
        });
    });
    // 5.5.4. anyOf
    tmp = null;
    test = !schema.anyOf || schema.anyOf.some(function (element) {
        local.tryCatchOnError(function () {
            // recurse - schema.anyOf
            local.validateBySwaggerSchema({
                data: data,
                modeSchema: options.modeSchema,
                prefix: options.prefix,
                schema: element,
                swaggerJson: options.swaggerJson
            });
            return true;
        }, local.nop);
        tmp = tmp || local.utility2._debugTryCatchError;
        return !tmp;
    });
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemOneOf',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.5. oneOf
    tmp = !schema.oneOf
        ? 1
        : 0;
    (schema.oneOf || []).some(function (element) {
        local.tryCatchOnError(function () {
            // recurse - schema.oneOf
            local.validateBySwaggerSchema({
                data: data,
                modeSchema: options.modeSchema,
                prefix: options.prefix,
                schema: element,
                swaggerJson: options.swaggerJson
            });
            tmp += 1;
        }, local.nop);
        return tmp > 1;
    });
    test = tmp === 1;
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemOneOf',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.6. not
    test = !schema.not || !local.tryCatchOnError(function () {
        // recurse - schema.not
        local.validateBySwaggerSchema({
            data: data,
            modeSchema: options.modeSchema,
            prefix: options.prefix,
            schema: schema.not,
            swaggerJson: options.swaggerJson
        });
        return true;
    }, local.nop);
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemNot',
        prefix: options.prefix,
        schema: schema
    });
    // 5.5.7. definitions
    local.nop();
    // validate data.$ref
    if (schema === local.swaggerSchemaJson.definitions.jsonReference) {
        local.validateBySwaggerSchema({
            modeDereference: true,
            modeSchema: options.modeSchema,
            prefix: options.prefix,
            schema: data,
            swaggerJson: options.swaggerJson
        });
    }
    return schema;
};

```

```javascript
/*
 * output from running code inside browser-console
 */
var mySchema = {
    required: ['myBoolean'],
    properties: {
        myArrayOfStrings: { items: { type: 'string' }, type: 'array' },
        myBoolean: { type: 'boolean' },
        myNumber: { type: 'number' },
        myString: { enum: ['hello world', 'bye world'], type: 'string' }
    }
};

undefined

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: ['foo', 'bar'],
        myBoolean: false,
        myString: 'hello world'
    },
    prefix: ['myData'],
    schema: mySchema
});

{required: Array(1), properties: {…}}

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: [1, 2],
        myBoolean: false
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.itemType - value myData["myArrayOfStrings"][0] = 1 is not a valid string
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27017)
    at assets.utility2.rollup.js:27097
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27095)
    at assets.utility2.rollup.js:27172
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
    at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27017
(anonymous) @ assets.utility2.rollup.js:27097
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27095
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM341:2

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: ['foo', 'bar'],
        myBoolean: null
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.objectRequired - object myData = {"myArrayOfStrings":["foo","bar"],"myBoolean":null} must have property "myBoolean"
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at assets.utility2.rollup.js:27158
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27156)
    at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
(anonymous) @ assets.utility2.rollup.js:27158
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27156
(anonymous) @ VM343:2

local.validateBySwaggerSchema({
    data: {
        myBoolean: false,
        myString: 'hello undefined'
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.itemEnum - string myData["myString"] = "hello undefined" can only have items from the list ["hello world","bye world"]
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27274)
    at assets.utility2.rollup.js:27172
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
    at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27274
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM345:2
```

> On Feb 18, 2018, at 9:42 AM, Jordan Harband <ljharb at gmail.com> wrote:
> 
> Your proposal is conceptually the same as a labelled break statement (ie, GOTO); if you want to follow the advice to avoid labels, I suspect it would apply to your proposal as well.
> 
> On Sat, Feb 17, 2018 at 4:44 PM, 李白|字一日 <calidion at gmail.com <mailto:calidion at gmail.com>> wrote:
> you can simply put these value handler pairs into an array.
> 
> 
> const a = [[0, function(){}], [1, function(){}]];
> let i = 0;
> 
> while (a[i++][0]) {
>   a[i - 1][1]()
>   break;
> }
> 
> 
> 2018-02-18 5:58 GMT+08:00 sagiv ben giat <sagiv.bengiat at gmail.com <mailto:sagiv.bengiat at gmail.com>>:
> >  What kind of argument is that? ESlint isn't a JavaScript runtime, it is fully configurable, and I don't see how it's at all relevant. 
> 
> I know ESLint can be configured, it was just an example for how `label`  statements are considered as poor design of code.
> 
> 
> 
> Sagiv B.G
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss>
> 
> 
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss>
> 
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180218/50d6b509/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Screen-Shot-2018-02-18-at-9.32.49-AM-compressor.png
Type: image/png
Size: 334732 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180218/50d6b509/attachment-0001.png>


More information about the es-discuss mailing list