import { AttributeSchema, AttributeType, AttributeTypeValues, COLLECTION_SCHEMA_NAME, CollectionAttributesSchema, IntegerAttributeTypes, NumberAttributeTypes, StringAttributeTypes, UniqueCollectionSchemaToCreate } from "../../types"; import {ValidationError} from "../../../utils/errors"; import {CollectionTokenPropertyPermissions} from "../../../substrate/extrinsics/unique/types"; import { validateAndParseSemverString, validateBoxedNumberWithDefault, validateFieldByType, validateLocalizedStringWithDefault, validateSingleTokenPropertyPermission, validateUrlTemplateString, validateUrlWithHashObject, isPlainObject, validateNumber, validateAttributeKey, } from "./common-validators"; import {RGB_REGEX, RGBA_REGEX} from "./constants"; export const validateValueVsAttributeType = (value: any, type: AttributeType, varName: string): value is typeof type => { isPlainObject(value, varName) if (NumberAttributeTypes.includes(type)) { const shouldBeInteger = IntegerAttributeTypes.includes(type) validateBoxedNumberWithDefault(value, shouldBeInteger, varName) if (type === AttributeType.boolean && ![0, 1].includes(value._)) { throw new ValidationError(`${varName}: should be a boolean integer: 0 or 1, got ${value._}`) } return true } if (StringAttributeTypes.includes(type)) { const canHaveLocalization = type === AttributeType.string validateLocalizedStringWithDefault(value, canHaveLocalization, varName) if (type === AttributeType.isoDate && isNaN(new Date(value._).valueOf())) { throw new ValidationError(`${varName}: should be a valid ISO Date (YYYY-MM-DD), got ${value._}`) } if (type === AttributeType.time && isNaN(new Date('1970-01-01T' + value._).valueOf())) { throw new ValidationError(`${varName}: should be a valid time in (hh:mm or hh:mm:ss), got ${value._}`) } if (type === AttributeType.colorRgba && (!value._.match(RGB_REGEX) && !value._.match(RGBA_REGEX))) { throw new ValidationError(`${varName}: should be a valid rgb or rgba color (like "#ff00ff00"), got ${value._}`) } return true } throw new ValidationError(`${varName}: unknown attribute type: ${type}`) } export const validateAttributesSchemaSingleAttribute = (attr: AttributeSchema, varName: string): attr is AttributeSchema => { isPlainObject(attr, varName) validateLocalizedStringWithDefault(attr.name, true, `${varName}.name`) if (attr.hasOwnProperty('optional') && typeof attr.optional !== 'boolean') throw new ValidationError(`${varName}.optional should be boolean when passed, got ${typeof attr.optional}: ${attr.optional}`) if (attr.hasOwnProperty('isArray') && typeof attr.isArray !== 'boolean') { throw new ValidationError(`${varName}.optional should be boolean when passed, got ${typeof attr.optional}: ${attr.optional}`) } if (!AttributeTypeValues.includes(attr.type)) throw new ValidationError(`${varName}.type should be a valid attribute type, got ${typeof attr.type}: ${attr.type}`) if (attr.hasOwnProperty('enumValues')) { isPlainObject(attr.enumValues, `${varName}.enumValues`) for (const key in attr.enumValues) { const localVarName = `${varName}.enumValues[${key}]` const intKey = parseInt(key) validateNumber(intKey, true, localVarName) validateValueVsAttributeType( attr.enumValues[intKey], attr.type, localVarName ) } } return true } export const validateCollectionAttributesSchema = (attributes: any, varName: string): attributes is CollectionAttributesSchema => { isPlainObject(attributes, varName) for (const key in attributes) { validateAttributeKey(key, varName) validateAttributesSchemaSingleAttribute(attributes[key], `${varName}["${key}"]`) } return true } export const validateUniqueCollectionSchema = (schema: any): schema is C => { isPlainObject(schema, 'Passed collection schema') if (schema.schemaName !== COLLECTION_SCHEMA_NAME.unique) throw new ValidationError(`schemaName is not valid (passed ${schema.schemaName})`) const schemaVersion = validateAndParseSemverString(schema.schemaVersion, 'schemaVersion') if (!schemaVersion.isEqual('1.0.0')) { throw new ValidationError(`collection schema has unsupported type: ${schemaVersion.toString()}`) } validateUrlWithHashObject(schema.coverPicture, 'coverPicture') if (schema.hasOwnProperty('coverPicturePreview')) { validateUrlWithHashObject(schema.coverPicturePreview, 'coverPicturePreview') } if (!schema.attributesSchemaVersion !== !schema.attributesSchema) { throw new ValidationError(`"attributesSchemaVersion" and "attributesSchema" should both be filled or both empty`) } if (schema.attributesSchemaVersion && schema.attributesSchema) { const attributesSchemaVersion = validateAndParseSemverString( schema.attributesSchemaVersion, 'attributesSchemaVersion', ) if (!attributesSchemaVersion.isEqual('1.0.0')) { throw new ValidationError(`collection attributes schema has unsupported type: ${attributesSchemaVersion.toString()}`) } validateCollectionAttributesSchema(schema.attributesSchema, 'attributesSchema') } isPlainObject(schema.image, 'image') validateUrlTemplateString(schema.image.urlTemplate, 'image') if (schema.hasOwnProperty('imagePreview')) { isPlainObject(schema.video, 'video') validateUrlTemplateString(schema.video.urlTemplate, 'video') } if (schema.hasOwnProperty('video')) { isPlainObject(schema.video, 'video') validateUrlTemplateString(schema.video.urlTemplate, 'video') } if (schema.hasOwnProperty('audio')) { isPlainObject(schema.audio, 'audio') validateUrlTemplateString(schema.audio.urlTemplate, 'audio') validateFieldByType(schema.audio, 'format', 'string', false, 'audio') validateFieldByType(schema.audio, 'isLossless', 'boolean', true, 'audio') } if (schema.hasOwnProperty('spatialObject')) { isPlainObject(schema.spatialObject, 'spatialObject') validateUrlTemplateString(schema.spatialObject.urlTemplate, 'spatialObject') validateFieldByType(schema.spatialObject, 'format', 'string', false, 'spatialObject') } return true } export const validateCollectionTokenPropertyPermissions = (tpps: any, varName: string = 'tokenPropertyPermissions'): tpps is CollectionTokenPropertyPermissions => { if (!Array.isArray(tpps)) throw new ValidationError(`${varName} should be an array, got ${typeof tpps}: ${tpps}`) tpps.forEach((tpp, index) => { validateSingleTokenPropertyPermission(tpp, `${varName}[${index}]`) }) return true }