001/*
002 * Copyright 2022-2025 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet.core;
018
019import javax.annotation.Nonnull;
020import java.lang.reflect.Parameter;
021import java.lang.reflect.Type;
022import java.util.Optional;
023
024/**
025 * Contract for converting request body bytes into a corresponding Java type.
026 * <p>
027 * For example, if your <em>Resource Methods</em> expect JSON request bodies like this (note the {@link com.soklet.annotation.RequestBody} annotation):
028 * <pre>{@code  @POST("/find-biggest")
029 * public Integer findBiggest(@RequestBody List<Integer> numbers) {
030 *   // JSON request body [1,2,3] results in 3 being returned
031 *   return Collections.max(numbers);
032 * }}</pre>
033 * <p>
034 * You might implement a {@link RequestBodyMarshaler} to accept JSON like this:
035 * <pre>{@code  SokletConfiguration config = SokletConfiguration.withServer(
036 *   DefaultServer.withPort(8080).build()
037 * ).requestBodyMarshaler(new RequestBodyMarshaler() {
038 *   // This example uses Google's GSON
039 *   static final Gson GSON = new Gson();
040 *
041 *   @Nonnull
042 *   @Override
043 *   public Optional<Object> marshalRequestBody(
044 *     @Nonnull Request request,
045 *     @Nonnull ResourceMethod resourceMethod,
046 *     @Nonnull Parameter parameter,
047 *     @Nonnull Type requestBodyType
048 *   ) {
049 *     // Let GSON turn the request body into an instance
050 *     // of the specified type.
051 *     //
052 *     // Note that this method has access to all runtime information
053 *     // about the request, which provides the opportunity to, for example,
054 *     // examine annotations on the method/parameter which might
055 *     // inform custom marshaling strategies.
056 *     return Optional.of(GSON.fromJson(
057 *       request.getBodyAsString().get(),
058 *       requestBodyType
059 *     ));
060 *   }
061 * }).build();}</pre>
062 * <p>
063 * See <a href="https://www.soklet.com/docs/request-handling#request-body">https://www.soklet.com/docs/request-handling#request-body</a> for detailed documentation.
064 *
065 * @author <a href="https://www.revetkn.com">Mark Allen</a>
066 */
067@FunctionalInterface
068public interface RequestBodyMarshaler {
069        /**
070         * Given a request, the <em>Resource Method</em> that will handle it, and a {@link com.soklet.annotation.RequestBody}-annotated parameter + its type, convert the request body bytes into an instance of type {@code requestBodyType}.
071         * <p>
072         * This instance will be injected by Soklet when it invokes the <em>Resource Method</em> to handle the request.
073         *
074         * @param request         the request whose body should be converted into a Java type
075         * @param resourceMethod  the <em>Resource Method</em> that is configured to handle the request
076         * @param parameter       the <em>Resource Method</em> parameter into which the returned instance will be injected
077         * @param requestBodyType the type of the <em>Resource Method</em> parameter (provided for convenience)
078         * @return the Java instance that corresponds to the request body bytes suitable for assignment to the <em>Resource Method</em> parameter, or {@link Optional#empty()} if no instance should be marshaled
079         */
080        @Nonnull
081        Optional<Object> marshalRequestBody(@Nonnull Request request,
082                                                                                                                                                        @Nonnull ResourceMethod resourceMethod,
083                                                                                                                                                        @Nonnull Parameter parameter,
084                                                                                                                                                        @Nonnull Type requestBodyType);
085}