All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.netflix.graphql.dgs.internal.utils.MultipartVariableMapper.kt Maven / Gradle / Ivy

There is a newer version: 10.0.1
Show newest version
/*
 * Copyright 2021 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.netflix.graphql.dgs.internal.utils

import org.springframework.web.multipart.MultipartFile
import java.util.regex.Pattern

/**
 * This implementation has borrowed heavily from graphql-servlet-java implementation of the variable mapper.
 * It handles populating the query variables with the files specified by object paths in the multi-part request.
 * Specifically, it takes each entry here '-F map='{ "0": ["variables.input.files.0"], "1": ["variables.input.files.1"] }',
 * and uses the object path, e.g., variables.input.files.0, to navigate to the appropriate path in the query variables, i.e.,
 * "variables": { "input": { "description": "test", "files": [null, null] } } }' and sets it to the file specified as
 * -F '[email protected]' -F '[email protected]'
 *
 * The resulting map of populated query variables is the output.
 * Original => "variables": { "input": { "description": "test", "files": [null, null] } }
 * Transformed => "variables": { "input": { "description": "test", "files": [file1.txt, file2.txt] } }
 */
object MultipartVariableMapper {
    private val PERIOD = Pattern.compile("\\.")

    private val MAP_MAPPER =
        object : Mapper> {
            override fun set(
                location: MutableMap,
                target: String,
                value: MultipartFile,
            ): Any? = location.put(target, value)

            override fun recurse(
                location: MutableMap,
                target: String,
            ): Any = location[target] ?: throw VariableMappingException("Path not found: $target")
        }

    private val LIST_MAPPER =
        object : Mapper> {
            override fun set(
                location: MutableList,
                target: String,
                value: MultipartFile,
            ): Any? = location.set(Integer.parseInt(target), value)

            override fun recurse(
                location: MutableList,
                target: String,
            ): Any = location[Integer.parseInt(target)]
        }

    internal interface Mapper {
        fun set(
            location: T,
            target: String,
            value: MultipartFile,
        ): Any?

        fun recurse(
            location: T,
            target: String,
        ): Any
    }

    @Suppress("UNCHECKED_CAST")
    fun mapVariable(
        objectPath: String,
        variables: MutableMap,
        part: MultipartFile,
    ) {
        val segments = PERIOD.split(objectPath)

        if (segments.size < 2) {
            throw VariableMappingException("object-path in map must have at least two segments")
        } else if ("variables" != segments[0]) {
            throw VariableMappingException("can only map into variables")
        }

        var currentLocation: Any = variables
        for (i in 1 until segments.size) {
            val segmentName = segments[i]
            if (i == segments.size - 1) {
                if (currentLocation is Map<*, *>) {
                    if (null != MAP_MAPPER.set(currentLocation as MutableMap, segmentName, part)) {
                        throw VariableMappingException("expected null value when mapping $objectPath")
                    }
                } else {
                    if (null != LIST_MAPPER.set(currentLocation as MutableList, segmentName, part)) {
                        throw VariableMappingException("expected null value when mapping $objectPath")
                    }
                }
            } else {
                currentLocation =
                    if (currentLocation is Map<*, *>) {
                        MAP_MAPPER.recurse(currentLocation as MutableMap, segmentName)
                    } else {
                        LIST_MAPPER.recurse(currentLocation as MutableList, segmentName)
                    }
            }
        }
    }
}

class VariableMappingException(
    message: String,
) : RuntimeException(message)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy