001/*
002 * (C) Copyright 2014-2018 Nuxeo (http://nuxeo.com/) and others.
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 * Contributors:
017 *     <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
018 *
019 */
020
021package org.nuxeo.ecm.platform.routing.core.io;
022
023import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
024import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON;
025import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE;
026
027import java.io.Closeable;
028import java.io.IOException;
029import java.io.OutputStream;
030
031import javax.inject.Inject;
032
033import org.apache.commons.lang3.StringUtils;
034import org.nuxeo.ecm.core.api.IdRef;
035import org.nuxeo.ecm.core.api.NuxeoPrincipal;
036import org.nuxeo.ecm.core.api.model.Property;
037import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter;
038import org.nuxeo.ecm.core.io.marshallers.json.OutputStreamWithJsonWriter;
039import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter;
040import org.nuxeo.ecm.core.io.registry.MarshallerRegistry;
041import org.nuxeo.ecm.core.io.registry.Writer;
042import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
043import org.nuxeo.ecm.core.io.registry.context.RenderingContext.SessionWrapper;
044import org.nuxeo.ecm.core.io.registry.reflect.Setup;
045import org.nuxeo.ecm.core.schema.SchemaManager;
046import org.nuxeo.ecm.core.schema.types.CompositeType;
047import org.nuxeo.ecm.core.schema.types.Field;
048import org.nuxeo.ecm.platform.routing.api.DocumentRoute;
049import org.nuxeo.ecm.platform.routing.core.impl.GraphRoute;
050import org.nuxeo.ecm.platform.usermanager.UserManager;
051
052import com.fasterxml.jackson.core.JsonGenerationException;
053import com.fasterxml.jackson.core.JsonGenerator;
054
055/**
056 * @since 7.2
057 */
058@Setup(mode = SINGLETON, priority = REFERENCE)
059public class DocumentRouteWriter extends ExtensibleEntityJsonWriter<DocumentRoute> {
060
061    public static final String ATTACHED_DOCUMENT_IDS = "attachedDocumentIds";
062
063    public static final String FETCH_ATTACHED_DOCUMENTS = ATTACHED_DOCUMENT_IDS;
064
065    public static final String ENTITY_TYPE = "workflow";
066
067    public static final String FETCH_INITATIOR = "initiator";
068
069    @Inject
070    UserManager userManager;
071
072    @Inject
073    private SchemaManager schemaManager;
074
075    public DocumentRouteWriter() {
076        super(ENTITY_TYPE, DocumentRoute.class);
077    }
078
079    @Override
080    protected void writeEntityBody(DocumentRoute item, JsonGenerator jg) throws IOException {
081        jg.writeStringField("id", item.getDocument().getId());
082        jg.writeStringField("name", item.getName());
083        jg.writeStringField("title", item.getTitle());
084        jg.writeStringField("state", item.getDocument().getCurrentLifeCycleState());
085        jg.writeStringField("workflowModelName", item.getModelName());
086        if (ctx.getFetched(ENTITY_TYPE).contains(FETCH_INITATIOR)) {
087            NuxeoPrincipal principal = userManager.getPrincipal(item.getInitiator());
088            if (principal != null) {
089                writeEntityField("initiator", principal, jg);
090            } else {
091                jg.writeStringField("initiator", item.getInitiator());
092            }
093        } else {
094            jg.writeStringField("initiator", item.getInitiator());
095        }
096
097        jg.writeArrayFieldStart(ATTACHED_DOCUMENT_IDS);
098        try (SessionWrapper wrapper = ctx.getSession(item.getDocument())) {
099            final boolean isFetchAttachedDocumentIds = ctx.getFetched(ENTITY_TYPE).contains(FETCH_ATTACHED_DOCUMENTS);
100            for (String docId : item.getAttachedDocuments()) {
101                if (isFetchAttachedDocumentIds) {
102                    IdRef idRef = new IdRef(docId);
103                    if (wrapper.getSession().exists(idRef)) {
104                        writeEntity(wrapper.getSession().getDocument(idRef), jg);
105                        break;
106                    }
107                }
108                jg.writeStartObject();
109                jg.writeStringField("id", docId);
110                jg.writeEndObject();
111            }
112        }
113        jg.writeEndArray();
114
115        if (item instanceof GraphRoute) {
116            jg.writeFieldName("variables");
117            jg.writeStartObject();
118
119            writeVariables(item, jg, registry, ctx, schemaManager);
120
121            jg.writeEndObject();
122            String graphResourceUrl = getGraphResourceURL(item, ctx);
123            jg.writeStringField("graphResource", graphResourceUrl);
124        }
125    }
126
127    /**
128     * @since 8.3
129     */
130    public static void writeVariables(DocumentRoute item, JsonGenerator jg, MarshallerRegistry registry,
131            RenderingContext ctx, SchemaManager schemaManager) throws IOException, JsonGenerationException {
132        String facet = (String) item.getDocument().getPropertyValue(GraphRoute.PROP_VARIABLES_FACET);
133        if (StringUtils.isNotBlank(facet)) {
134
135            CompositeType type = schemaManager.getFacet(facet);
136            if (type != null) {
137                boolean hasFacet = item.getDocument().hasFacet(facet);
138
139                Writer<Property> propertyWriter = registry.getWriter(ctx, Property.class, APPLICATION_JSON_TYPE);
140                // provides the current route to the property marshaller
141                try (Closeable resource = ctx.wrap()
142                                             .with(DocumentModelJsonWriter.ENTITY_TYPE, item.getDocument())
143                                             .open()) {
144                    for (Field f : type.getFields()) {
145                        String name = f.getName().getLocalName();
146                        Property property = hasFacet ? item.getDocument().getProperty(name) : null;
147                        OutputStream out = new OutputStreamWithJsonWriter(jg);
148                        jg.writeFieldName(name);
149                        propertyWriter.write(property, Property.class, Property.class, APPLICATION_JSON_TYPE, out);
150                    }
151                }
152            }
153        }
154    }
155
156    /**
157     * @since 10.1
158     */
159    public static String getGraphResourceURL(DocumentRoute route, RenderingContext ctx) {
160        StringBuilder sb = new StringBuilder(ctx.getBaseUrl());
161        sb.append("api/v1/");
162        if (route.isValidated()) {
163            // it is a model
164            sb.append("workflowModel/").append(route.getDocument().getName());
165        } else {
166            // it is an instance
167            sb.append("workflow/").append(route.getDocument().getId());
168        }
169        sb.append("/graph");
170        return sb.toString();
171    }
172}