Skip to content

Commit

Permalink
chore: convert to functions (#167)
Browse files Browse the repository at this point in the history
* chore: migrate validate objects to validate functions

* chore: dont expanse components always

* chore: add items specific validations as functions

* chore: uupdate common-schemas

* chore: fix build

---------

Signed-off-by: pedrotambo <pftambo@gmail.com>
  • Loading branch information
pedrotambo committed Feb 9, 2023
1 parent 5f698c4 commit 6ae9fb8
Show file tree
Hide file tree
Showing 42 changed files with 1,315 additions and 1,521 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"dependencies": {
"@dcl/content-hash-tree": "^1.1.3",
"@dcl/hashing": "1.1.3",
"@dcl/schemas": "^6.4.0",
"@dcl/schemas": "^6.4.1",
"@dcl/urn-resolver": "2.0.3",
"@well-known-components/interfaces": "1.1.3",
"@well-known-components/thegraph-component": "^1.2.0",
Expand Down
9 changes: 4 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ContentValidatorComponents, OK, Validator } from './types'
import { validations } from './validations'
import { validateFns } from './validations'

export { createTheGraphClient } from './the-graph-client/the-graph-client'
export * from './types'
export * from './validations'

Expand All @@ -15,8 +16,8 @@ export const createValidator = (

return {
validate: async (deployment) => {
for (const validation of validations) {
const result = await validation.validate(components, deployment)
for (const validate of validateFns) {
const result = await validate(components, deployment)
if (!result.ok) {
logs.debug(`Validation failed:\n${result.errors?.join('\n')}`)
return result
Expand All @@ -26,5 +27,3 @@ export const createValidator = (
}
}
}

export { createTheGraphClient } from './the-graph-client/the-graph-client'
20 changes: 4 additions & 16 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,10 @@ export type ValidationResponse = {
/**
* @public
*/
export type Validation = {
validate: (
components: ContentValidatorComponents,
deployment: DeploymentToValidate
) => ValidationResponse | Promise<ValidationResponse>
}

/**
* @public
*/
export type ConditionalValidation = {
predicate: (
components: ContentValidatorComponents,
deployment: DeploymentToValidate
) => ValidationResponse | Promise<ValidationResponse>
}
export type ValidateFn = (
components: ContentValidatorComponents,
deployment: DeploymentToValidate
) => ValidationResponse | Promise<ValidationResponse>

/**
* @public
Expand Down
14 changes: 6 additions & 8 deletions src/validations/ADR45.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Entity } from '@dcl/schemas'
import { ContentValidatorComponents, OK, Validation, validationFailed } from '../types'
import { ContentValidatorComponents, DeploymentToValidate, OK, validationFailed } from '../types'
import { ADR_45_TIMESTAMP } from './timestamps'

const entityIsNotVersion3 = (entity: Entity) => entity.version !== 'v3'
Expand All @@ -10,13 +10,11 @@ const entityWasDeployedAfterADR45 = (entity: Entity) => entity.timestamp > ADR_4
* Validate that entity meets ADR-45 validations
* @public
*/
export const adr45: Validation = {
validate: async (components: ContentValidatorComponents, deployment) => {
const { entity } = deployment
export function adr45(components: ContentValidatorComponents, deployment: DeploymentToValidate) {
const { entity } = deployment

if (entityIsNotVersion3(entity) && entityWasDeployedAfterADR45(entity))
return validationFailed('Only entities v3 are allowed after ADR-45')
if (entityIsNotVersion3(entity) && entityWasDeployedAfterADR45(entity))
return validationFailed('Only entities v3 are allowed after ADR-45')

return OK
}
return OK
}
37 changes: 17 additions & 20 deletions src/validations/access-checker/access.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Entity, EntityType } from '@dcl/schemas'
import { DeploymentToValidate, OK, Validation, validationFailed } from '../../types'
import { ContentValidatorComponents, DeploymentToValidate, OK, ValidateFn, validationFailed } from '../../types'
import { LEGACY_CONTENT_MIGRATION_TIMESTAMP } from '../timestamps'
import { emotes } from './emotes'
import { profiles } from './profiles'
import { scenes } from './scenes'
import { stores } from './stores'
import { wearables } from './wearables'

const accessCheckers: Record<EntityType, Validation> = {
const accessCheckers: Record<EntityType, ValidateFn> = {
[EntityType.PROFILE]: profiles,
[EntityType.SCENE]: scenes,
[EntityType.WEARABLE]: wearables,
Expand All @@ -19,27 +19,24 @@ const accessCheckers: Record<EntityType, Validation> = {
* Validate that the pointers are valid, and that the Ethereum address has write access to them
* @public
*/
export const access: Validation = {
validate: async (components, deployment: DeploymentToValidate) => {
if ((await components.config.getString('IGNORE_BLOCKCHAIN_ACCESS_CHECKS')) === 'true') {
return OK
}

const { externalCalls } = components
const deployedBeforeDCLLaunch = deployment.entity.timestamp <= LEGACY_CONTENT_MIGRATION_TIMESTAMP
const address = externalCalls.ownerAddress(deployment.auditInfo)
export async function access(components: ContentValidatorComponents, deployment: DeploymentToValidate) {
if ((await components.config.getString('IGNORE_BLOCKCHAIN_ACCESS_CHECKS')) === 'true') {
return OK
}

// Default scenes were removed from the Content Servers after https://github.com/decentraland/catalyst/issues/878
if (isDefaultScene(deployment.entity)) {
return validationFailed(
`Scene pointers should only contain two integers separated by a comma, for example (10,10) or (120,-45).`
)
}
// Legacy entities still need to be synchronized
if (deployedBeforeDCLLaunch && externalCalls.isAddressOwnedByDecentraland(address)) return OK
const deployedBeforeDCLLaunch = deployment.entity.timestamp <= LEGACY_CONTENT_MIGRATION_TIMESTAMP
const address = components.externalCalls.ownerAddress(deployment.auditInfo)

return accessCheckers[deployment.entity.type].validate(components, deployment)
// Default scenes were removed from the Content Servers after https://github.com/decentraland/catalyst/issues/878
if (isDefaultScene(deployment.entity)) {
return validationFailed(
`Scene pointers should only contain two integers separated by a comma, for example (10,10) or (120,-45).`
)
}
// Legacy entities still need to be synchronized
if (deployedBeforeDCLLaunch && components.externalCalls.isAddressOwnedByDecentraland(address)) return OK

return accessCheckers[deployment.entity.type](components, deployment)
}

function isDefaultScene(entity: Entity) {
Expand Down
21 changes: 9 additions & 12 deletions src/validations/access-checker/emotes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { ContentValidatorComponents } from '../..'
import { Validation } from '../../types'
import { ContentValidatorComponents, DeploymentToValidate } from '../..'
import { itemsValidation } from './items/items'

export const emotes: Validation = {
validate: async (
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'theGraphClient'>,
deployment
) => {
return itemsValidation.validate(components, deployment, [
'blockchain-collection-v2-asset',
'blockchain-collection-third-party'
])
}
export async function emotes(
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'theGraphClient'>,
deployment: DeploymentToValidate
) {
return itemsValidation(components, deployment, [
'blockchain-collection-v2-asset',
'blockchain-collection-third-party'
])
}
6 changes: 3 additions & 3 deletions src/validations/access-checker/items/collection-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,13 @@ async function checkCollectionAccess(

export const v1andV2collectionAssetValidation: AssetValidation = {
async validateAsset(
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'theGraphClient' | 'subGraphs'>,
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'subGraphs' | 'theGraphClient'>,
asset: BlockchainCollectionV1Asset | BlockchainCollectionV2Asset,
deployment: DeploymentToValidate
) {
const { externalCalls, subGraphs } = components
const { externalCalls, subGraphs, logs } = components
const ethAddress = externalCalls.ownerAddress(deployment.auditInfo)
const logger = components.logs.getLogger('collection asset access validation')
const logger = logs.getLogger('collection asset access validation')
// L1 or L2 so contractAddress is present
const collection = asset.contractAddress!
const network = asset.network
Expand Down
58 changes: 28 additions & 30 deletions src/validations/access-checker/items/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,40 @@ async function parseUrnNoFail(urn: string): Promise<SupportedAsset | null> {
return null
}

export const itemsValidation = {
validate: async (
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'theGraphClient'>,
deployment: DeploymentToValidate,
validUrnTypesForItem: UrnType[]
) => {
const { pointers } = deployment.entity
export async function itemsValidation(
components: Pick<ContentValidatorComponents, 'externalCalls' | 'logs' | 'theGraphClient'>,
deployment: DeploymentToValidate,
validUrnTypesForItem: UrnType[]
) {
const { pointers } = deployment.entity

const resolvedPointers: SupportedAsset[] = []
// deduplicate pointer resolution
for (const pointer of pointers) {
const parsed = await parseUrnNoFail(pointer)
if (!parsed)
return validationFailed(
`Item pointers should be a urn, for example (urn:decentraland:{protocol}:collections-v2:{contract(0x[a-fA-F0-9]+)}:{id}). Invalid pointer: (${pointer})`
)
const resolvedPointers: SupportedAsset[] = []
// deduplicate pointer resolution
for (const pointer of pointers) {
const parsed = await parseUrnNoFail(pointer)
if (!parsed)
return validationFailed(
`Item pointers should be a urn, for example (urn:decentraland:{protocol}:collections-v2:{contract(0x[a-fA-F0-9]+)}:{id}). Invalid pointer: (${pointer})`
)

if (!alreadySeen(resolvedPointers, parsed)) resolvedPointers.push(parsed)
}
if (!alreadySeen(resolvedPointers, parsed)) resolvedPointers.push(parsed)
}

if (resolvedPointers.length > 1)
return validationFailed(`Only one pointer is allowed when you create an item. Received: ${pointers}`)
if (resolvedPointers.length > 1)
return validationFailed(`Only one pointer is allowed when you create an item. Received: ${pointers}`)

const parsedAsset = resolvedPointers[0]
const parsedAsset = resolvedPointers[0]

if (!validUrnTypesForItem.includes(parsedAsset.type)) {
return validationFailed(
`For the entity type: ${deployment.entity.type}, the asset with urn type: ${parsedAsset.type} is invalid. Valid urn types for this entity: ${validUrnTypesForItem}`
)
}
if (!validUrnTypesForItem.includes(parsedAsset.type)) {
return validationFailed(
`For the entity type: ${deployment.entity.type}, the asset with urn type: ${parsedAsset.type} is invalid. Valid urn types for this entity: ${validUrnTypesForItem}`
)
}

for (const validation of assetValidations) {
if (validation.canValidate(parsedAsset)) {
return validation.validateAsset(components, parsedAsset, deployment)
}
for (const validation of assetValidations) {
if (validation.canValidate(parsedAsset)) {
return validation.validateAsset(components, parsedAsset, deployment)
}
throw new Error('This should never happen. There is no validations for the asset.')
}
throw new Error('This should never happen. There is no validations for the asset.')
}
Loading

0 comments on commit 6ae9fb8

Please sign in to comment.