• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cartesi / rollups-explorer / 10667377776

02 Sep 2024 12:42PM UTC coverage: 93.408% (-0.4%) from 93.768%
10667377776

Pull #232

github

nevendyulgerov
feat(apps/web): Tweak import validations
Pull Request #232: #229 Add import and export for specifications

1217 of 1450 branches covered (83.93%)

Branch coverage included in aggregate %.

515 of 603 new or added lines in 8 files covered. (85.41%)

3 existing lines in 1 file now uncovered.

13053 of 13827 relevant lines covered (94.4%)

38.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.13
/apps/web/src/components/specification/transfer/validators/v1.ts
1
import {
1✔
2
    isArray,
1✔
3
    isNonEmptyString,
1✔
4
    isNotString,
1✔
5
    isNumber,
1✔
6
    isString,
1✔
7
    isUndefined,
1✔
8
} from "ramda-adjunct";
1✔
9
import { parseAbi, parseAbiParameters } from "viem";
1✔
10
import {
1✔
11
    logicalOperators,
1✔
12
    operators,
1✔
13
    Specification,
1✔
14
    SPECIFICATION_TRANSFER_NAME,
1✔
15
    SpecificationTransfer,
1✔
16
    inputProperties,
1✔
17
} from "../../types";
1✔
18
import { formatAbi } from "abitype";
1✔
19
import { prepareSignatures } from "../../utils";
1✔
20
import { specModeValidation, specNameValidation } from "../../form/validations";
1✔
21
import { uniq } from "ramda";
1✔
22
import { patchField } from "../../conditionals";
1✔
23

1✔
24
const inputPropertyValues = inputProperties.map((p) => p.value);
1✔
25

1✔
26
const validateSpecifications = (
1✔
27
    specifications: Specification[],
17✔
28
): Promise<void> => {
17✔
29
    return new Promise((resolve, reject) => {
17✔
30
        const errors = uniq(
17✔
31
            specifications.reduce(
17✔
32
                (accumulator: (string | null)[], specification) => {
17✔
33
                    const nameError = specNameValidation(specification.name);
33✔
34
                    const modeError = specModeValidation(specification.mode);
33✔
35
                    const idError = isNonEmptyString(specification.id)
33✔
36
                        ? null
31✔
37
                        : "Id field with string value is required.";
2✔
38
                    const timestampError = isNumber(specification.timestamp)
33✔
39
                        ? null
31✔
40
                        : "Timestamp field with numeric value is required.";
2✔
41

33✔
42
                    let abiError = null;
33✔
43
                    if (specification.mode === "json_abi") {
33✔
44
                        const formattedAbi = formatAbi(
15✔
45
                            specification.abi ?? [],
15!
46
                        ).join("\n");
15✔
47
                        const items = prepareSignatures(formattedAbi);
15✔
48

15✔
49
                        try {
15✔
50
                            parseAbi(items);
15✔
51
                        } catch (error: any) {
15!
NEW
52
                            abiError = error.message;
×
NEW
53
                        }
×
54
                    } else if (specification.mode === "abi_params") {
33✔
55
                        try {
14✔
56
                            parseAbiParameters(specification.abiParams);
14✔
57
                        } catch (error: any) {
14✔
58
                            abiError = error.message;
1✔
59
                        }
1✔
60
                    }
14✔
61

32✔
62
                    const conditionsErrors = (
32✔
63
                        specification?.conditionals ?? []
33!
64
                    ).reduce(
33✔
65
                        (
33✔
66
                            conditionAccumulator: (string | null)[],
32✔
67
                            condition,
32✔
68
                        ) => {
32✔
69
                            const logicalOperatorError = logicalOperators.some(
32✔
70
                                (o) => o.value === condition.logicalOperator,
32✔
71
                            )
32✔
72
                                ? null
30✔
73
                                : `Invalid logical operator for condition. Valid values are: ${logicalOperators
2✔
74
                                      .map((o) => o.value)
2✔
75
                                      .join(", ")}`;
2✔
76

32✔
77
                            const schemaErrors = condition.conditions.reduce(
32✔
78
                                (
32✔
79
                                    conditionAccumulator: (string | null)[],
32✔
80
                                    conditionItem,
32✔
81
                                ) => {
32✔
82
                                    const error =
32✔
83
                                        isNonEmptyString(conditionItem.field) &&
32✔
84
                                        inputPropertyValues.includes(
30✔
85
                                            patchField(conditionItem.field),
30✔
86
                                        ) &&
30✔
87
                                        operators.some(
30✔
88
                                            (o) =>
30✔
89
                                                o.value ===
30✔
90
                                                conditionItem.operator,
30✔
91
                                        ) &&
30✔
92
                                        isNonEmptyString(conditionItem.value)
28✔
93
                                            ? null
26✔
94
                                            : "Invalid condition schema.";
6✔
95

32✔
96
                                    return [...conditionAccumulator, error];
32✔
97
                                },
32✔
98
                                [],
32✔
99
                            );
32✔
100

32✔
101
                            return [
32✔
102
                                ...conditionAccumulator,
32✔
103
                                logicalOperatorError,
32✔
104
                                ...schemaErrors,
32✔
105
                            ];
32✔
106
                        },
32✔
107
                        [],
33✔
108
                    );
33✔
109

33✔
110
                    let sliceErrors: (string | null)[] = [];
33✔
111
                    if (specification.mode === "abi_params") {
33✔
112
                        const sliceTargetError =
14✔
113
                            isString(specification.sliceTarget) &&
14✔
114
                            (specification?.sliceInstructions ?? []).length > 0
14!
115
                                ? (specification?.sliceInstructions ?? []).some(
14!
116
                                      (s) =>
14✔
117
                                          s.name === specification.sliceTarget,
14✔
118
                                  )
14✔
119
                                    ? null
12✔
120
                                    : "Invalid slice target."
2!
NEW
121
                                : null;
×
122

14✔
123
                        sliceErrors = [
14✔
124
                            ...(specification?.sliceInstructions ?? []).reduce(
14!
125
                                (
14✔
126
                                    sliceAccumulator: (string | null)[],
14✔
127
                                    sliceInstruction,
14✔
128
                                ) => {
14✔
129
                                    const error =
14✔
130
                                        isNumber(sliceInstruction.from) &&
14✔
131
                                        (isUndefined(sliceInstruction.to) ||
13✔
132
                                            isNumber(sliceInstruction.to)) &&
13✔
133
                                        (isUndefined(sliceInstruction.name) ||
12✔
134
                                            isNonEmptyString(
12✔
135
                                                sliceInstruction.name,
12✔
136
                                            )) &&
12✔
137
                                        (isUndefined(sliceInstruction.type) ||
11✔
138
                                            isNonEmptyString(
11✔
139
                                                sliceInstruction.type,
11✔
140
                                            ))
11✔
141
                                            ? null
10✔
142
                                            : "Invalid slice schema.";
4✔
143

14✔
144
                                    return [...sliceAccumulator, error];
14✔
145
                                },
14✔
146
                                [],
14✔
147
                            ),
14✔
148
                            sliceTargetError,
14✔
149
                        ];
14✔
150
                    }
14✔
151

32✔
152
                    const errors = [
32✔
153
                        nameError,
32✔
154
                        modeError,
32✔
155
                        idError,
32✔
156
                        timestampError,
32✔
157
                        abiError,
32✔
158
                        ...conditionsErrors,
32✔
159
                        ...sliceErrors,
32✔
160
                    ].filter((error) => isNonEmptyString(error));
32✔
161

32✔
162
                    return [...accumulator, ...errors];
32✔
163
                },
33✔
164
                [],
17✔
165
            ),
17✔
166
        );
17✔
167

17✔
168
        if (errors.length > 0) {
17✔
169
            return reject(errors);
15✔
170
        }
15✔
171

1✔
172
        resolve();
1✔
173
    });
17✔
174
};
17✔
175

1✔
176
const validateTopLevelSchema = (
1✔
177
    specificationImport: SpecificationTransfer,
20✔
178
): Promise<void> => {
20✔
179
    return new Promise((resolve, reject) => {
20✔
180
        if (
20✔
181
            isNotString(specificationImport.name) ||
20✔
182
            specificationImport.name !== SPECIFICATION_TRANSFER_NAME
20✔
183
        ) {
20✔
184
            return reject([
1✔
185
                `Missing or invalid 'name' field. The import must include a 'name' field with the string value of '${SPECIFICATION_TRANSFER_NAME}'.`,
1✔
186
            ]);
1✔
187
        }
1✔
188

19✔
189
        if (!isNumber(specificationImport.timestamp)) {
20✔
190
            return reject([
2✔
191
                "Missing 'timestamp' field. The import must include a 'timestamp' field with numeric value.",
2✔
192
            ]);
2✔
193
        }
2✔
194

17✔
195
        if (
17✔
196
            !isArray(specificationImport.specifications) ||
17✔
197
            specificationImport.specifications.length === 0
17✔
198
        ) {
20!
NEW
199
            return reject(["No specifications exist in the import."]);
×
NEW
200
        }
✔
201

17✔
202
        resolve();
17✔
203
    });
20✔
204
};
20✔
205

1✔
206
export const validateSpecificationImport = async (
1✔
207
    specificationImport: SpecificationTransfer,
20✔
208
): Promise<void> => {
20✔
209
    await validateTopLevelSchema(specificationImport);
20✔
210
    return await validateSpecifications(specificationImport.specifications);
17✔
211
};
17✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc