CAUSEWAY-3676: adds config properties to control whether/how resources can be downloaded
Also uses spring.graphql.path as the location of the resource controller
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
index 036f2ba..067b072 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
@@ -2433,6 +2433,55 @@
private String zonedDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssXXX";
}
+ /**
+ * The different ways in which resources ({@link org.apache.causeway.applib.value.Blob} bytes,
+ * {@link org.apache.causeway.applib.value.Clob} chars, grids and icons) can be downloaded from the
+ * resource controller.
+ */
+ public enum ResponseType {
+ /**
+ * Do not allow the resources to be downloaded at all. This is the default.
+ *
+ * <p>
+ * In this case any {@link org.apache.causeway.applib.value.Blob} and
+ * {@link org.apache.causeway.applib.value.Clob} properties will <i>not</i> provide a link to
+ * the URL. Attempting to download from the resource controller will result in a 403 (forbidden).
+ * </p>
+ */
+ FORBIDDEN,
+ /**
+ * Allows resources to be downloaded directly.
+ *
+ * <p>
+ * <b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
+ * are secured appropriately.
+ * </p>
+ */
+ DIRECT,
+ /**
+ * Allows resources to be downloaded as attachments (using <code>Content-Disposition</code> header).
+ *
+ * <p>
+ * <b>IMPORTANT: </b> if enabling this configuration property, make sure that the <code>ResourcesController</code> endpoints
+ * are secured appropriately.
+ * </p>
+ */
+ ATTACHMENT,
+ ;
+ }
+
+ @Getter
+ private final Resources resources = new Resources();
+ @Data
+ public static class Resources {
+ /**
+ * How resources ({@link org.apache.causeway.applib.value.Blob} bytes,
+ * {@link org.apache.causeway.applib.value.Clob} chars, grids and icons) can be downloaded from the
+ * resource controller.
+ */
+ private ResponseType responseType = ResponseType.FORBIDDEN;
+ }
+
@Getter
private final Authentication authentication = new Authentication();
@Data
@@ -2454,7 +2503,6 @@
private List<String> roles;
}
}
-
}
private final Restfulobjects restfulobjects = new Restfulobjects();
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMeta.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMeta.java
index 7249f31..7c5de51 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMeta.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMeta.java
@@ -25,6 +25,7 @@
import org.apache.causeway.applib.services.bookmark.Bookmark;
import org.apache.causeway.applib.services.bookmark.BookmarkService;
import org.apache.causeway.applib.services.metamodel.BeanSort;
+import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet;
import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
import org.apache.causeway.core.metamodel.facets.object.layout.LayoutFacet;
@@ -34,6 +35,8 @@
import org.apache.causeway.viewer.graphql.model.context.Context;
import org.apache.causeway.viewer.graphql.model.mmproviders.ObjectSpecificationProvider;
+import org.springframework.beans.factory.annotation.Value;
+
import lombok.val;
public class GqlvMeta extends GqlvAbstractCustom {
@@ -43,12 +46,14 @@
private final GqlvMetaLogicalTypeName metaLogicalTypeName;
private final GqlvMetaVersion metaVersion;
private final GqlvMetaTitle metaTitle;
- private final GqlvMetaIcon metaIconName;
+ private final GqlvMetaIcon metaIcon;
private final GqlvMetaCssClass metaCssClass;
private final GqlvMetaLayout metaLayout;
private final GqlvMetaGrid metaGrid;
private final GqlvMetaSaveAs metaSaveAs;
+ private final CausewayConfiguration.Viewer.Graphql graphqlConfiguration;
+
public GqlvMeta(
final Holder holder,
final Context context
@@ -56,12 +61,14 @@
super(TypeNames.metaTypeNameFor(holder.getObjectSpecification()), context);
this.holder = holder;
+ this.graphqlConfiguration = context.causewayConfiguration.getViewer().getGraphql();
+
if(isBuilt()) {
this.metaId = null;
this.metaLogicalTypeName = null;
this.metaVersion = null;
this.metaTitle = null;
- this.metaIconName = null;
+ this.metaIcon = null;
this.metaCssClass = null;
this.metaLayout = null;
this.metaGrid = null;
@@ -73,16 +80,21 @@
addChildFieldFor(this.metaLogicalTypeName = new GqlvMetaLogicalTypeName(context));
addChildFieldFor(this.metaVersion = isEntity() ? new GqlvMetaVersion(context) : null);
addChildFieldFor(this.metaTitle = new GqlvMetaTitle(context));
- addChildFieldFor(this.metaIconName = new GqlvMetaIcon(context));
addChildFieldFor(this.metaCssClass = new GqlvMetaCssClass(context));
addChildFieldFor(this.metaLayout = new GqlvMetaLayout(context));
- addChildFieldFor(this.metaGrid = new GqlvMetaGrid(context));
addChildFieldFor(this.metaSaveAs = new GqlvMetaSaveAs(context));
- val fieldName = context.causewayConfiguration.getViewer().getGraphql().getMetaData().getFieldName();
+ addChildFieldFor(this.metaIcon = isResourceNotForbidden() ? new GqlvMetaIcon(context) : null);
+ addChildFieldFor(this.metaGrid = isResourceNotForbidden() ? new GqlvMetaGrid(context) : null);
+
+ val fieldName = graphqlConfiguration.getMetaData().getFieldName();
buildObjectTypeAndField(fieldName);
}
+ private boolean isResourceNotForbidden() {
+ return graphqlConfiguration.getResources().getResponseType() != CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN;
+ }
+
private boolean isEntity() {
return holder.getObjectSpecification().getBeanSort() == BeanSort.ENTITY;
}
@@ -90,6 +102,7 @@
@Override
protected void addDataFetchersForChildren() {
if (metaId == null) {
+ // none of the fields will have been initialized
return;
}
metaId.addDataFetcher(this);
@@ -98,17 +111,21 @@
metaVersion.addDataFetcher(this);
}
metaTitle.addDataFetcher(this);
- metaIconName.addDataFetcher(this);
metaCssClass.addDataFetcher(this);
metaLayout.addDataFetcher(this);
- metaGrid.addDataFetcher(this);
metaSaveAs.addDataFetcher(this);
+ if (metaGrid != null) {
+ metaGrid.addDataFetcher(this);
+ }
+ if (metaIcon != null) {
+ metaIcon.addDataFetcher(this);
+ }
}
@Override
public Object fetchData(final DataFetchingEnvironment environment) {
return context.bookmarkService.bookmarkFor(environment.getSource())
- .map(bookmark -> new Fetcher(bookmark, context.bookmarkService, context.objectManager))
+ .map(bookmark -> new Fetcher(bookmark, context.bookmarkService, context.objectManager, context.causewayConfiguration))
.orElseThrow();
}
@@ -121,14 +138,20 @@
private final Bookmark bookmark;
private final BookmarkService bookmarkService;
private final ObjectManager objectManager;
+ private final CausewayConfiguration causewayConfiguration;
+ private final String graphqlPath;
Fetcher(
final Bookmark bookmark,
final BookmarkService bookmarkService,
- final ObjectManager objectManager) {
+ final ObjectManager objectManager,
+ final CausewayConfiguration causewayConfiguration
+ ) {
this.bookmark = bookmark;
this.bookmarkService = bookmarkService;
this.objectManager = objectManager;
+ this.causewayConfiguration = causewayConfiguration;
+ this.graphqlPath = causewayConfiguration.valueOf("spring.graphql.path").orElse("/graphql");
}
public String logicalTypeName(){
@@ -193,8 +216,8 @@
return managedObject()
.flatMap(Bookmarkable::getBookmark
).map(bookmark -> String.format(
- "///%s/object/%s:%s/_meta/%s",
- "graphql", bookmark.getLogicalTypeName(), bookmark.getIdentifier(), resource) )
+ "//%s/object/%s:%s/%s/%s",
+ graphqlPath, bookmark.getLogicalTypeName(), bookmark.getIdentifier(), causewayConfiguration.getViewer().getGraphql().getMetaData().getFieldName(), resource) )
.orElse(null);
}
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlob.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlob.java
index 6380205..794bcca 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlob.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlob.java
@@ -22,6 +22,7 @@
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
+import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.causeway.viewer.graphql.model.context.Context;
@@ -39,13 +40,16 @@
final GqlvPropertyGetBlobMimeType blobMimeType;
final GqlvPropertyGetBlobName blobBytes;
+ private final CausewayConfiguration.Viewer.Graphql graphqlConfiguration;
+
public GqlvPropertyGetBlob(
final Holder holder,
final Context context) {
super(TypeNames.propertyBlobTypeNameFor(holder.getObjectSpecification(), holder.getObjectMember()), context);
-
this.holder = holder;
+ this.graphqlConfiguration = context.causewayConfiguration.getViewer().getGraphql();
+
if (isBuilt()) {
// type already exists, nothing else to do.
this.blobName = null;
@@ -56,7 +60,7 @@
addChildFieldFor(blobName = new GqlvPropertyGetBlobBytes(this, context));
addChildFieldFor(blobMimeType = new GqlvPropertyGetBlobMimeType(this, context));
- addChildFieldFor(blobBytes = new GqlvPropertyGetBlobName(this, context));
+ addChildFieldFor(blobBytes = isResourceNotForbidden() ? new GqlvPropertyGetBlobName(this, context) : null);
setField(newFieldDefinition()
.name("get")
@@ -64,6 +68,10 @@
.build());
}
+ private boolean isResourceNotForbidden() {
+ return graphqlConfiguration.getResources().getResponseType() != CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN;
+ }
+
@Override
protected Object fetchData(final DataFetchingEnvironment dataFetchingEnvironment) {
return BookmarkedPojo.sourceFrom(dataFetchingEnvironment, context);
@@ -76,7 +84,9 @@
}
blobName.addDataFetcher(this);
blobMimeType.addDataFetcher(this);
- blobBytes.addDataFetcher(this);
+ if (blobBytes != null) {
+ blobBytes.addDataFetcher(this);
+ }
}
@Override
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlobBytes.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlobBytes.java
index 2c1fe83..5c6fee5 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlobBytes.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetBlobBytes.java
@@ -30,10 +30,14 @@
public class GqlvPropertyGetBlobBytes extends GqlvPropertyGetBlobAbstract {
+ private final String graphqlPath;
+
public GqlvPropertyGetBlobBytes(
final Holder holder,
final Context context) {
super(holder, context, "bytes");
+
+ this.graphqlPath = context.causewayConfiguration.valueOf("spring.graphql.path").orElse("/graphql");
}
@Override
@@ -42,7 +46,7 @@
Optional<Bookmark> bookmarkIfAny = context.bookmarkService.bookmarkFor(sourcePojo);
return bookmarkIfAny.map(x -> String.format(
- "///%s/object/%s:%s/%s/blobBytes", "graphql", x.getLogicalTypeName(), x.getIdentifier(), holder.getObjectAssociation().getId())).orElse(null);
+ "//%s/object/%s:%s/%s/blobBytes", graphqlPath, x.getLogicalTypeName(), x.getIdentifier(), holder.getObjectAssociation().getId())).orElse(null);
}
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClob.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClob.java
index 6983f85..75e756d 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClob.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClob.java
@@ -22,6 +22,7 @@
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
+import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.causeway.viewer.graphql.model.context.Context;
@@ -35,9 +36,11 @@
{
final Holder holder;
- final GqlvPropertyGetClobChars clobChars;
- final GqlvPropertyGetClobMimeType clobMimeType;
final GqlvPropertyGetClobName clobName;
+ final GqlvPropertyGetClobMimeType clobMimeType;
+ final GqlvPropertyGetClobChars clobChars;
+
+ private final CausewayConfiguration.Viewer.Graphql graphqlConfiguration;
public GqlvPropertyGetClob(
final Holder holder,
@@ -45,16 +48,19 @@
super(TypeNames.propertyBlobTypeNameFor(holder.getObjectSpecification(), holder.getObjectMember()), context);
this.holder = holder;
- if(isBuilt()) {
- this.clobChars = null;
- this.clobMimeType = null;
+ this.graphqlConfiguration = context.causewayConfiguration.getViewer().getGraphql();
+
+ if (isBuilt()) {
+ // type already exists, nothing else to do.
this.clobName = null;
+ this.clobMimeType = null;
+ this.clobChars = null;
return;
}
- addChildFieldFor(clobChars = new GqlvPropertyGetClobChars(this, context));
- addChildFieldFor(clobMimeType = new GqlvPropertyGetClobMimeType(this, context));
addChildFieldFor(clobName = new GqlvPropertyGetClobName(this, context));
+ addChildFieldFor(clobMimeType = new GqlvPropertyGetClobMimeType(this, context));
+ addChildFieldFor(clobChars = isResourceNotForbidden() ? new GqlvPropertyGetClobChars(this, context) : null);
setField(newFieldDefinition()
.name("get")
@@ -62,6 +68,10 @@
.build());
}
+ private boolean isResourceNotForbidden() {
+ return graphqlConfiguration.getResources().getResponseType() != CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN;
+ }
+
@Override
protected Object fetchData(final DataFetchingEnvironment dataFetchingEnvironment) {
return BookmarkedPojo.sourceFrom(dataFetchingEnvironment, context);
@@ -69,12 +79,14 @@
@Override
protected void addDataFetchersForChildren() {
- if(clobChars == null) {
+ if(clobName == null) {
return;
}
- clobChars.addDataFetcher(this);
- clobMimeType.addDataFetcher(this);
clobName.addDataFetcher(this);
+ clobMimeType.addDataFetcher(this);
+ if(clobChars != null) {
+ clobChars.addDataFetcher(this);
+ }
}
@Override
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClobChars.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClobChars.java
index fd59b02..6acff31 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClobChars.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvPropertyGetClobChars.java
@@ -30,10 +30,14 @@
public class GqlvPropertyGetClobChars extends GqlvPropertyGetClobAbstract {
+ private final String graphqlPath;
+
public GqlvPropertyGetClobChars(
final Holder holder,
final Context context) {
- super(holder, context, "bytes");
+ super(holder, context, "chars");
+
+ this.graphqlPath = context.causewayConfiguration.valueOf("spring.graphql.path").orElse("/graphql");
}
@Override
@@ -42,7 +46,7 @@
Optional<Bookmark> bookmarkIfAny = context.bookmarkService.bookmarkFor(sourcePojo);
return bookmarkIfAny.map(x -> String.format(
- "///%s/object/%s:%s/%s/clobChars", "graphql", x.getLogicalTypeName(), x.getIdentifier(), holder.getObjectAssociation().getId())).orElse(null);
+ "//%s/object/%s:%s/%s/clobChars", graphqlPath, x.getLogicalTypeName(), x.getIdentifier(), holder.getObjectAssociation().getId())).orElse(null);
}
diff --git a/viewers/graphql/test/src/test/resources/application-test.properties b/viewers/graphql/test/src/test/resources/application-test.properties
index cbfd204..514fd18 100644
--- a/viewers/graphql/test/src/test/resources/application-test.properties
+++ b/viewers/graphql/test/src/test/resources/application-test.properties
@@ -26,4 +26,6 @@
decorator.datasource.datasource-proxy.json-format=false
# Enable Query Metrics
-decorator.datasource.datasource-proxy.count-query=false
\ No newline at end of file
+decorator.datasource.datasource-proxy.count-query=false
+
+causeway.viewer.graphql.resources.responseType=ATTACHMENT
\ No newline at end of file
diff --git a/viewers/graphql/test/src/test/resources/schema.gql b/viewers/graphql/test/src/test/resources/schema.gql
index 2e857f1..99035dc 100644
--- a/viewers/graphql/test/src/test/resources/schema.gql
+++ b/viewers/graphql/test/src/test/resources/schema.gql
@@ -218,8 +218,6 @@
type causeway_applib_DomainObjectList__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -266,8 +264,6 @@
type causeway_applib_FacetGroupNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -298,8 +294,6 @@
type causeway_applib_ParameterNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -341,8 +335,6 @@
type causeway_applib_PropertyNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -394,8 +386,6 @@
type causeway_applib_RoleMemento__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -436,8 +426,6 @@
type causeway_applib_TypeNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -498,8 +486,6 @@
type causeway_applib_UserMemento__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -614,8 +600,6 @@
type causeway_applib_node_ActionNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -666,8 +650,6 @@
type causeway_applib_node_CollectionNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -717,8 +699,6 @@
type causeway_applib_node_FacetAttrNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -760,8 +740,6 @@
type causeway_applib_node_FacetNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -805,8 +783,6 @@
type causeway_conf_ConfigurationProperty__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -848,8 +824,6 @@
type causeway_conf_ConfigurationViewmodel__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -881,8 +855,6 @@
type causeway_feat_ApplicationFeatureViewModel__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -942,8 +914,6 @@
type causeway_feat_ApplicationNamespace__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1019,8 +989,6 @@
type causeway_feat_ApplicationTypeAction__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1102,8 +1070,6 @@
type causeway_feat_ApplicationTypeCollection__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1156,8 +1122,6 @@
type causeway_feat_ApplicationTypeMember__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1223,8 +1187,6 @@
type causeway_feat_ApplicationTypeProperty__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1310,8 +1272,6 @@
type causeway_feat_ApplicationType__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1412,8 +1372,6 @@
type causeway_schema_metamodel_v2_DomainClassDto__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1472,8 +1430,6 @@
type causeway_security_LoginRedirect__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1520,8 +1476,6 @@
type causeway_testing_fixtures_FixtureResult__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1552,8 +1506,6 @@
type java_lang_Runnable__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1567,8 +1519,6 @@
type java_util_Map__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1582,8 +1532,6 @@
type java_util_SortedMap__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1597,8 +1545,6 @@
type java_util_concurrent_Callable__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1612,8 +1558,6 @@
type java_util_function_BiFunction__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1627,8 +1571,6 @@
type java_util_function_Consumer__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1642,8 +1584,6 @@
type java_util_function_Function__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1657,8 +1597,6 @@
type java_util_stream_Stream__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1680,8 +1618,6 @@
type org_apache_causeway_core_metamodel_inspect_model_MMNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1712,8 +1648,6 @@
type org_apache_causeway_core_metamodel_inspect_model_MemberNode__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -1756,8 +1690,6 @@
type org_apache_causeway_testing_fixtures_applib_fixturescripts_FixtureScript__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -2752,8 +2684,6 @@
type university_dept_Department__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -2927,8 +2857,6 @@
type university_dept_DeptHead__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -3007,8 +2935,6 @@
type university_dept_StaffMember__gqlv_meta {
cssClass: String
- grid: String
- icon: String
id: String!
layout: String
logicalTypeName: String!
@@ -3048,7 +2974,6 @@
type university_dept_StaffMember__photo__gqlv_property_blob {
bytes: String
mimeType: String
- name: String
}
type university_dept_Staff__createStaffMember__department__gqlv_action_parameter {
diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
index 8e792e9..4ea4ff9 100644
--- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
+++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
@@ -7,12 +7,14 @@
import org.apache.causeway.applib.layout.grid.Grid;
import org.apache.causeway.commons.io.JaxbUtils;
+import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.metamodel.facets.object.grid.GridFacet;
import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
@@ -30,17 +32,26 @@
import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
-import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.val;
@RestController()
@RequestMapping("/graphql/object")
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
public class ResourceController {
private final BookmarkService bookmarkService;
private final ObjectManager objectManager;
+ private final CausewayConfiguration.Viewer.Graphql graphqlConfiguration;
+
+ @Inject
+ public ResourceController(
+ final BookmarkService bookmarkService,
+ final ObjectManager objectManager,
+ final CausewayConfiguration causewayConfiguration) {
+ this.bookmarkService = bookmarkService;
+ this.objectManager = objectManager;
+ this.graphqlConfiguration = causewayConfiguration.getViewer().getGraphql();
+ }
@GetMapping(value = "/{logicalTypeName}:{id}/{propertyId}/blobBytes")
public ResponseEntity<byte[]> propertyBlobBytes(
@@ -48,14 +59,26 @@
@PathVariable final String id,
@PathVariable final String propertyId
) {
+ val responseType = graphqlConfiguration.getResources().getResponseType();
+
+ // TODO: perhaps a filter would factor this check out?
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+
return valueOfProperty(logicalTypeName, id, propertyId)
.filter(Blob.class::isInstance)
.map(Blob.class::cast)
- .map(blob -> ResponseEntity.ok()
- .contentType(MediaType.asMediaType(MimeType.valueOf(blob.getMimeType().toString())))
- .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(blob.getName()).build().toString())
- .contentLength(blob.getBytes().length)
- .body(blob.getBytes()))
+ .map(blob -> {
+ val bodyBuilder = ResponseEntity.ok()
+ .contentType(MediaType.asMediaType(MimeType.valueOf(blob.getMimeType().toString())));
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.ATTACHMENT) {
+ bodyBuilder
+ .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(blob.getName()).build().toString())
+ .contentLength(blob.getBytes().length);
+ }
+ return bodyBuilder.body(blob.getBytes());
+ })
.orElse(ResponseEntity.notFound().build());
}
@@ -65,50 +88,91 @@
@PathVariable final String id,
@PathVariable final String propertyId
) {
+ val responseType = graphqlConfiguration.getResources().getResponseType();
+
+ // TODO: perhaps a filter would factor this check out?
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+
return valueOfProperty(logicalTypeName, id, propertyId)
.filter(Clob.class::isInstance)
.map(Clob.class::cast)
- .map(clob -> ResponseEntity.ok()
- .contentType(MediaType.asMediaType(MimeType.valueOf(clob.getMimeType().toString())))
- .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(clob.getName()).build().toString())
- .contentLength(clob.getChars().length())
- .body(clob.getChars()))
+ .map(clob -> {
+ val bodyBuilder = ResponseEntity.ok()
+ .contentType(MediaType.asMediaType(MimeType.valueOf(clob.getMimeType().toString())));
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.ATTACHMENT) {
+ bodyBuilder.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(clob.getName()).build().toString())
+ .contentLength(clob.getChars().length());
+ }
+ return bodyBuilder.body(clob.getChars());
+ })
.orElse(ResponseEntity.notFound().build());
}
- @GetMapping(value = "/{logicalTypeName}:{id}/_meta/grid")
+ @GetMapping(value = "/{logicalTypeName}:{id}/{_meta}/grid")
public ResponseEntity<String> grid(
@PathVariable final String logicalTypeName,
- @PathVariable final String id
+ @PathVariable final String id,
+ @PathVariable final String _meta
) {
+ val responseType = graphqlConfiguration.getResources().getResponseType();
+
+ // TODO: perhaps a filter would factor this check out?
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+
+ if (!_meta.equals(graphqlConfiguration.getMetaData().getFieldName())) {
+ return ResponseEntity.notFound().build();
+ }
+
return lookup(logicalTypeName, id)
.map(ResourceController::gridOf)
.filter(Objects::nonNull)
.map(JaxbUtils::toStringUtf8)
.map(x -> x.replaceAll("(\r\n)", "\n"))
- .map(gridText -> ResponseEntity.ok()
- .contentType(MediaType.APPLICATION_XML)
- .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(logicalTypeName + ".layout.xml").build().toString())
- .contentLength(gridText.length())
- .body(gridText))
+ .map(gridText -> {
+ val bodyBuilder = ResponseEntity.ok()
+ .contentType(MediaType.APPLICATION_XML);
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.ATTACHMENT) {
+ bodyBuilder
+ .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(logicalTypeName + ".layout.xml").build().toString())
+ .contentLength(gridText.length());
+ }
+ return bodyBuilder.body(gridText);
+ })
.orElse(ResponseEntity.notFound().build());
}
- @GetMapping(value = "/{logicalTypeName}:{id}/_meta/icon")
+ @GetMapping(value = "/{logicalTypeName}:{id}/{_meta}/icon")
public ResponseEntity<byte[]> icon(
@PathVariable final String logicalTypeName,
- @PathVariable final String id
+ @PathVariable final String id,
+ @PathVariable final String _meta
) {
+ // TODO: perhaps a filter would factor this check out?
+ val responseType = graphqlConfiguration.getResources().getResponseType();
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.FORBIDDEN) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+ if (!_meta.equals(graphqlConfiguration.getMetaData().getFieldName())) {
+ return ResponseEntity.notFound().build();
+ }
+
return lookup(logicalTypeName, id)
.map(ManagedObject::getIcon)
.filter(Objects::nonNull)
.map(objectIcon -> {
val bytes = objectIcon.asBytes();
- return ResponseEntity.ok()
- .contentType(MediaType.parseMediaType(objectIcon.getMimeType().getMimeType().toString()))
- .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(logicalTypeName + ".png").build().toString())
- .contentLength(bytes.length)
- .body(bytes);
+ val bodyBuilder = ResponseEntity.ok()
+ .contentType(MediaType.parseMediaType(objectIcon.getMimeType().getMimeType().toString()));
+ if (responseType == CausewayConfiguration.Viewer.Graphql.ResponseType.ATTACHMENT) {
+ bodyBuilder
+ .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(logicalTypeName + ".png").build().toString())
+ .contentLength(bytes.length);
+ }
+ return bodyBuilder.body(bytes);
})
.orElse(ResponseEntity.notFound().build());
}
@@ -119,11 +183,6 @@
return facet != null ? facet.getGrid(managedObject) : null;
}
- @Nullable
- private static ObjectIcon iconOf(ManagedObject managedObject) {
- return managedObject.getIcon();
- }
-
private Optional<Object> valueOfProperty(String logicalTypeName, String id, String propertyId) {
return lookup(logicalTypeName, id)
.map(managedObject -> ManagedObjectAndPropertyIfAny.of(managedObject, managedObject.getSpecification().getProperty(propertyId)))