File size: 6,010 Bytes
cfbbc1a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParseArrayPipe = void 0;
const tslib_1 = require("tslib");
const injectable_decorator_1 = require("../decorators/core/injectable.decorator");
const optional_decorator_1 = require("../decorators/core/optional.decorator");
const http_status_enum_1 = require("../enums/http-status.enum");
const http_error_by_code_util_1 = require("../utils/http-error-by-code.util");
const shared_utils_1 = require("../utils/shared.utils");
const validation_pipe_1 = require("./validation.pipe");
const VALIDATION_ERROR_MESSAGE = 'Validation failed (parsable array expected)';
const DEFAULT_ARRAY_SEPARATOR = ',';
/**
 * Defines the built-in ParseArray Pipe
 *
 * @see [Built-in Pipes](https://docs.nestjs.com/pipes#built-in-pipes)
 *
 * @publicApi
 */
let ParseArrayPipe = class ParseArrayPipe {
    constructor(options = {}) {
        this.options = options;
        this.validationPipe = new validation_pipe_1.ValidationPipe({
            transform: true,
            validateCustomDecorators: true,
            ...options,
        });
        const { exceptionFactory, errorHttpStatusCode = http_status_enum_1.HttpStatus.BAD_REQUEST } = options;
        this.exceptionFactory =
            exceptionFactory ||
                (error => new http_error_by_code_util_1.HttpErrorByCode[errorHttpStatusCode](error));
    }
    /**
     * Method that accesses and performs optional transformation on argument for
     * in-flight requests.
     *
     * @param value currently processed route argument
     * @param metadata contains metadata about the currently processed route argument
     */
    async transform(value, metadata) {
        if (!value && !this.options.optional) {
            throw this.exceptionFactory(VALIDATION_ERROR_MESSAGE);
        }
        else if ((0, shared_utils_1.isNil)(value) && this.options.optional) {
            return value;
        }
        if (!Array.isArray(value)) {
            if (!(0, shared_utils_1.isString)(value)) {
                throw this.exceptionFactory(VALIDATION_ERROR_MESSAGE);
            }
            else {
                try {
                    value = value
                        .trim()
                        .split(this.options.separator || DEFAULT_ARRAY_SEPARATOR);
                }
                catch {
                    throw this.exceptionFactory(VALIDATION_ERROR_MESSAGE);
                }
            }
        }
        if (this.options.items) {
            const validationMetadata = {
                metatype: this.options.items,
                type: 'query',
            };
            const isExpectedTypePrimitive = this.isExpectedTypePrimitive();
            const toClassInstance = (item, index) => {
                if (this.options.items !== String) {
                    try {
                        item = JSON.parse(item);
                    }
                    catch {
                        // Do nothing
                    }
                }
                if (isExpectedTypePrimitive) {
                    return this.validatePrimitive(item, index);
                }
                return this.validationPipe.transform(item, validationMetadata);
            };
            if (this.options.stopAtFirstError === false) {
                // strict compare to "false" to make sure
                // that this option is disabled by default
                let errors = [];
                const targetArray = value;
                for (let i = 0; i < targetArray.length; i++) {
                    try {
                        targetArray[i] = await toClassInstance(targetArray[i]);
                    }
                    catch (err) {
                        let message;
                        if (err.getResponse) {
                            const response = err.getResponse();
                            if (Array.isArray(response.message)) {
                                message = response.message.map((item) => `[${i}] ${item}`);
                            }
                            else {
                                message = `[${i}] ${response.message}`;
                            }
                        }
                        else {
                            message = err;
                        }
                        errors = errors.concat(message);
                    }
                }
                if (errors.length > 0) {
                    throw this.exceptionFactory(errors);
                }
                return targetArray;
            }
            else {
                value = await Promise.all(value.map(toClassInstance));
            }
        }
        return value;
    }
    isExpectedTypePrimitive() {
        return [Boolean, Number, String].includes(this.options.items);
    }
    validatePrimitive(originalValue, index) {
        if (this.options.items === Number) {
            const value = originalValue !== null && originalValue !== '' ? +originalValue : NaN;
            if (isNaN(value)) {
                throw this.exceptionFactory(`${(0, shared_utils_1.isUndefined)(index) ? '' : `[${index}] `}item must be a number`);
            }
            return value;
        }
        else if (this.options.items === String) {
            if (!(0, shared_utils_1.isString)(originalValue)) {
                return `${originalValue}`;
            }
        }
        else if (this.options.items === Boolean) {
            if (typeof originalValue !== 'boolean') {
                throw this.exceptionFactory(`${(0, shared_utils_1.isUndefined)(index) ? '' : `[${index}] `}item must be a boolean value`);
            }
        }
        return originalValue;
    }
};
exports.ParseArrayPipe = ParseArrayPipe;
exports.ParseArrayPipe = ParseArrayPipe = tslib_1.__decorate([
    (0, injectable_decorator_1.Injectable)(),
    tslib_1.__param(0, (0, optional_decorator_1.Optional)()),
    tslib_1.__metadata("design:paramtypes", [Object])
], ParseArrayPipe);