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}