Skip to content

Commit

Permalink
Send events when creating entities in multipart post
Browse files Browse the repository at this point in the history
  • Loading branch information
rschev committed Aug 8, 2024
1 parent ab3eb87 commit 5d61e6f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
import org.springframework.content.commons.storeservice.StoreInfo;
import org.springframework.content.commons.storeservice.Stores;
import org.springframework.content.rest.config.RestConfiguration;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.data.repository.support.RepositoryInvokerFactory;
import org.springframework.data.rest.core.event.AfterCreateEvent;
import org.springframework.data.rest.core.event.BeforeCreateEvent;
import org.springframework.data.rest.core.mapping.ResourceType;
import org.springframework.data.rest.core.support.SelfLinkProvider;
import org.springframework.data.rest.webmvc.*;
Expand Down Expand Up @@ -61,9 +64,10 @@ public class RepositoryEntityMultipartController {
private Stores stores;
private SelfLinkProvider selfLinkProvider;
private HttpHeadersPreparer headersPreparer;
private ApplicationEventPublisher publisher;

@Autowired
public RepositoryEntityMultipartController(RestConfiguration restConfig, RepositoryInvokerFactory repoInvokerFactory, ContentPropertyToRequestMappingContext requestMappingContext, SelfLinkProvider selfLinkProvider, Stores stores, MappingContext mappingContext, ContentPropertyToExportedContext exportedMappingContext, StoreByteRangeHttpRequestHandler byteRangeRestRequestHandler, @Qualifier("entityMultipartHttpMessageConverterConfigurer") RepositoryRestConfigurer configurer, HttpHeadersPreparer headersPreparer) {
public RepositoryEntityMultipartController(RestConfiguration restConfig, RepositoryInvokerFactory repoInvokerFactory, ContentPropertyToRequestMappingContext requestMappingContext, SelfLinkProvider selfLinkProvider, Stores stores, MappingContext mappingContext, ContentPropertyToExportedContext exportedMappingContext, StoreByteRangeHttpRequestHandler byteRangeRestRequestHandler, @Qualifier("entityMultipartHttpMessageConverterConfigurer") RepositoryRestConfigurer configurer, HttpHeadersPreparer headersPreparer, ApplicationEventPublisher publisher) {
this.restConfig = restConfig;
this.repoInvokerFactory = repoInvokerFactory;
this.requestMappingContext = requestMappingContext;
Expand All @@ -73,6 +77,7 @@ public RepositoryEntityMultipartController(RestConfiguration restConfig, Reposit
this.exportedMappingContext = exportedMappingContext;
this.byteRangeRestRequestHandler = byteRangeRestRequestHandler;
this.headersPreparer = headersPreparer;
this.publisher = publisher;
}

@SuppressWarnings({ "rawtypes", "unchecked" })
Expand All @@ -97,9 +102,14 @@ public ResponseEntity<RepresentationModel<?>> createEntityAndContent(RootResourc

String store = pathSegments[1];

BeforeCreateEvent beforeCreate = new BeforeCreateEvent(savedEntity);
publisher.publishEvent(beforeCreate);
// Save the entity and re-assign the result to savedEntity, so that it exists in the repository before content is added to it.
savedEntity = repoInvokerFactory.getInvokerFor(domainType).invokeSave(savedEntity);

AfterCreateEvent afterCreate = new AfterCreateEvent(savedEntity);
publisher.publishEvent(afterCreate);

StoreInfo info = this.stores.getStore(Store.class, StoreUtils.withStorePath(store));
if (info != null) {
ContentStoreContentService service = new ContentStoreContentService(restConfig, info, repoInvokerFactory.getInvokerFor(domainType), mappingContext, exportedMappingContext, byteRangeRestRequestHandler);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package internal.org.springframework.content.rest.support;

import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.event.AbstractRepositoryEventListener;

@Configuration
public class EventListenerConfig {

@Bean
TestEventListener testListener() {
return new TestEventListener();
}

public static class TestEventListener extends AbstractRepositoryEventListener<Object> {
private final List<Object> beforeCreate = new ArrayList<>();
private final List<Object> afterCreate = new ArrayList<>();

@Override
protected void onBeforeCreate(Object entity) {
beforeCreate.add(entity);
}

@Override
protected void onAfterCreate(Object entity) {
afterCreate.add(entity);
}

public void clear() {
this.afterCreate.clear();
this.beforeCreate.clear();
}

public List<Object> getBeforeCreate() {
return beforeCreate;
}

public List<Object> getAfterCreate() {
return afterCreate;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.github.paulcwarren.ginkgo4j.Ginkgo4jConfiguration;
import internal.org.springframework.content.rest.support.EventListenerConfig.TestEventListener;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.Optional;
Expand Down Expand Up @@ -35,15 +37,16 @@
import com.github.paulcwarren.ginkgo4j.Ginkgo4jSpringRunner;

@RunWith(Ginkgo4jSpringRunner.class)
// @Ginkgo4jConfiguration(threads=1)
@Ginkgo4jConfiguration(threads=1)
@WebAppConfiguration
@ContextConfiguration(classes = {
StoreConfig.class,
EntityConfig.class,
DelegatingWebMvcConfiguration.class,
RepositoryRestMvcConfiguration.class,
RestConfiguration.class,
HypermediaConfiguration.class
HypermediaConfiguration.class,
EventListenerConfig.class
})
@Transactional
@ActiveProfiles("store")
Expand Down Expand Up @@ -88,6 +91,9 @@ public class ContentEntityRestEndpointsIT {
@Autowired
TestStore store;

@Autowired
TestEventListener eventListener;

@Autowired
private WebApplicationContext context;

Expand Down Expand Up @@ -430,6 +436,29 @@ public class ContentEntityRestEndpointsIT {
});
});

Context("given a multipart/form POST and an event listener", () -> {
BeforeEach(() -> {
eventListener.clear();
});
It("should create a new entity and fire the onBeforeCreate/onAfterCreate events", () -> {

// POST the entity
MockHttpServletResponse response = mvc.perform(multipart("/testEntity3s")
.param("name", "foo foo")
.param("hidden", "bar bar")
.param("ying", "yang")
.param("things", "one", "two"))

.andExpect(status().isCreated())
.andReturn().getResponse();

assertThat(eventListener.getBeforeCreate().size(), is(1));
assertThat(eventListener.getAfterCreate().size(), is(1));
assertThat(((TestEntity3) eventListener.getBeforeCreate().get(0)).getName(), is("foo foo"));
assertThat(((TestEntity3) eventListener.getAfterCreate().get(0)).getName(), is("foo foo"));
});
});

Context("given a multipart/form POST to an entity with a mapped content property", () -> {
It("should create a new entity and its content and respond with a 201 Created", () -> {
// assert content does not exist
Expand Down

0 comments on commit 5d61e6f

Please sign in to comment.