TranslatedWorkflowStepDTOSerializer.java

/*
 * Copyright 2026 Global Crop Diversity Trust
 * Licensed under the Apache License, Version 2.0
 * See LICENSE file in project root folder or http://www.apache.org/licenses/LICENSE-2.0
 */

package org.gringlobal.custom.json.serializer;

import java.io.IOException;
import java.util.Map;

import org.gringlobal.api.model.TranslatedWorkflowStepDTO;
import org.gringlobal.api.model.TranslatedWorkflowStepDTO.TranslatedWorkflowActionStepDTO;
import org.gringlobal.api.model.WorkflowStepDTO;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;

/**
 * Custom serializer for TranslatedWorkflowStepDTO that inlines (unwraps) the
 * fields of the underlying {@link WorkflowStepDTO} and also injects the "_class"
 * property with the simple class name of the DTO subtype. This avoids Jackson's
 * {@link SerializationFeature#FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS} error when using {@link JsonUnwrapped} together
 * with EXISTING_PROPERTY type info on the wrapped type.
 */
public class TranslatedWorkflowStepDTOSerializer extends JsonSerializer<TranslatedWorkflowStepDTO<?>> {

	@Override
	public void serializeWithType(TranslatedWorkflowStepDTO<?> value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
		ObjectMapper mapper = (ObjectMapper) gen.getCodec();
		gen.writeStartObject();
		var entity = value != null ? value.getEntity() : null;
		if (entity != null) {
			for (Map.Entry<String, JsonNode> e : mapper.valueToTree(entity).properties()) {
				gen.writeFieldName(e.getKey());
				mapper.writeValue(gen, e.getValue());
			}
		}

		// Write Translated fields
		if (value != null) {
			if (value.getTitle() != null) {
				gen.writeStringField("title", value.getTitle());
			}
			if (value.getDescription() != null) {
				gen.writeStringField("description", value.getDescription());
			}
			if (value instanceof TranslatedWorkflowActionStepDTO) {
				var tas = (TranslatedWorkflowActionStepDTO) value;
				gen.writeStringField("conditionMessage", tas.getConditionMessage());
				gen.writeStringField("actionNote", tas.getActionNote());
			}
		}

		gen.writeEndObject();
	}

	@Override
	public void serialize(TranslatedWorkflowStepDTO<?> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
		ObjectMapper mapper = (ObjectMapper) gen.getCodec();
		gen.writeStartObject();
		var entity = value != null ? value.getEntity() : null;
		if (entity != null) {
			for (Map.Entry<String, JsonNode> e : mapper.valueToTree(entity).properties()) {
				gen.writeFieldName(e.getKey());
				mapper.writeValue(gen, e.getValue());
			}
		}

		// Write Translated fields
		if (value != null) {
			if (value.getTitle() != null) {
				gen.writeStringField("title", value.getTitle());
			}
			if (value.getDescription() != null) {
				gen.writeStringField("description", value.getDescription());
			}
			if (value instanceof TranslatedWorkflowActionStepDTO) {
				var tas = (TranslatedWorkflowActionStepDTO) value;
				gen.writeStringField("conditionMessage", tas.getConditionMessage());
				gen.writeStringField("actionNote", tas.getActionNote());
			}
		}

		gen.writeEndObject();
	}
}