Skip to content

Commit

Permalink
adds Google Analytics measurement API integration
Browse files Browse the repository at this point in the history
  • Loading branch information
jdbranham committed Dec 27, 2024
1 parent bbae610 commit 3d3b557
Show file tree
Hide file tree
Showing 33 changed files with 1,406 additions and 42 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/log4j*.xml
18 changes: 15 additions & 3 deletions module-flow/src/main/java/net/savantly/nexus/flow/api/FlowApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;

import org.apache.causeway.applib.services.eventbus.EventBusService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -25,6 +27,8 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.savantly.nexus.flow.api.events.submitFormJson.SubmitFormJsonEvent;
import net.savantly.nexus.flow.api.events.submitFormJson.SubmitFormJsonEventData;
import net.savantly.nexus.flow.dom.files.FileEntities;
import net.savantly.nexus.flow.dom.files.FileEntityDto;
import net.savantly.nexus.flow.dom.flowDefinition.FlowDefinitions;
Expand All @@ -39,6 +43,7 @@ public class FlowApi {
private final FlowService flowService;
private final FileEntities files;
private final ObjectMapper mapper;
private final EventBusService eventBusService;

@GetMapping("/definitions")
public FlowDefinitions getFlowDefinitions() {
Expand All @@ -61,7 +66,7 @@ public void executeFlow(@PathVariable String flowId, @RequestBody Object payload
public ResponseEntity submitFormJson(@PathVariable String formId, @RequestBody Object payload,
@RequestHeader(name = "api-key", required = false) String apiKey,
@RequestParam(name = "g-recaptcha-response", required = false) String recaptcha,
@RequestHeader(name = "X-Forwarded-For", required = false) String clientIP)
@RequestHeader(name = "X-Forwarded-For", required = false) String clientIP, HttpServletRequest request)
throws JsonProcessingException {
log.info("Requested to submit form: {}", formId);

Expand Down Expand Up @@ -90,11 +95,18 @@ public ResponseEntity submitFormJson(@PathVariable String formId, @RequestBody O
}

try {
flowService.submitForm(formId, payloadMap, apiKey, recaptcha, clientIP);
return ResponseEntity.ok().build();
var submission = flowService.submitForm(formId, payloadMap, apiKey, recaptcha, clientIP);
log.info("publishing event for formId: {}", formId);
var eventData = SubmitFormJsonEventData.builder().submission(submission).clientIP(clientIP)
.request(request).build();
var event = new SubmitFormJsonEvent(eventData);
eventBusService.post(event);
return ResponseEntity.ok().body(Map.of("success", true));
} catch (IllegalArgumentException e) {
var errBody = Map.of("error", e.getMessage());
return ResponseEntity.badRequest().body(errBody);
} catch (NoSuchElementException e) {
return ResponseEntity.notFound().build();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import net.savantly.nexus.flow.dom.flowNode.FlowNodeDescriptor;
import net.savantly.nexus.flow.dom.flowNode.FlowNodeDiscoveryService;
import net.savantly.nexus.flow.dom.form.Forms;
import net.savantly.nexus.flow.dom.formSubmission.FormSubmission;

@RequiredArgsConstructor
public class FlowService {
Expand All @@ -30,9 +31,10 @@ public void executeFlow(String flowId, Object payload, String apiKey) {
flowDefinitions.executeFlow(flowId, payload, apiKey);
}

public void submitForm(String formId, Map<String, Object> payload, String apiKey, String recaptcha, String clientIP)
public FormSubmission submitForm(String formId, Map<String, Object> payload, String apiKey, String recaptcha,
String clientIP)
throws JsonProcessingException {
forms.submitForm(formId, payload, apiKey, recaptcha, clientIP);
return forms.submitForm(formId, payload, apiKey, recaptcha, clientIP);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.savantly.nexus.flow.api.events.submitFormJson;

import org.springframework.context.ApplicationEvent;

import lombok.Getter;

public class SubmitFormJsonEvent extends ApplicationEvent {

private static final long serialVersionUID = 1L;

@Getter
private final SubmitFormJsonEventData source;

public SubmitFormJsonEvent(SubmitFormJsonEventData source) {
super(source);
this.source = source;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.savantly.nexus.flow.api.events.submitFormJson;

import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;
import lombok.Data;
import net.savantly.nexus.flow.dom.formSubmission.FormSubmission;

@Data
@Builder
public class SubmitFormJsonEventData {

private final FormSubmission submission;
private final String clientIP;
private final HttpServletRequest request;
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ public void triggerDestinationForSubmission(FormSubmission submission, String de
}
}

public void submitForm(Form form, Map<String, Object> payload, String apiKey, String recaptcha, String clientIP)
public FormSubmission submitForm(Form form, Map<String, Object> payload, String apiKey, String recaptcha,
String clientIP)
throws JsonProcessingException {

if (!form.isPublicForm()) {
Expand Down Expand Up @@ -147,6 +148,7 @@ public void submitForm(Form form, Map<String, Object> payload, String apiKey, St

}
}
return submission;
}

private ZonedDateTime now() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import net.savantly.nexus.common.types.Name;
import net.savantly.nexus.flow.FlowModule;
import net.savantly.nexus.flow.dom.destination.DestinationHookFactory;
import net.savantly.nexus.flow.dom.formSubmission.FormSubmission;
import net.savantly.nexus.organizations.dom.organization.Organization;

@Named(FlowModule.NAMESPACE + ".Forms")
Expand Down Expand Up @@ -58,15 +59,15 @@ public Optional<Form> findById(final String id) {
}

@Programmatic
public void submitForm(final String formId, final Map<String, Object> payload, final String apiKey,
public FormSubmission submitForm(final String formId, final Map<String, Object> payload, final String apiKey,
String recaptcha, String clientIP)
throws JsonProcessingException {
var form = repository.findById(formId).orElseThrow();
log.info("submitting form: " + form.getName());
formSubmissionProxy.submitForm(form, payload, apiKey, recaptcha, clientIP);
var submission = formSubmissionProxy.submitForm(form, payload, apiKey, recaptcha, clientIP);

log.info("executing hooks for form: " + form.getName());

return submission;
}

}
114 changes: 114 additions & 0 deletions module-google-analytics-flow/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>nexus-command</artifactId>
<groupId>net.savantly</groupId>
<version>3.0.9-SNAPSHOT</version>
</parent>

<artifactId>google-analytics-flow-module</artifactId>
<name>Nexus Command - GA Flow Module</name>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/java</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</build>

<dependencies>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>common-types-module</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>encryption-module</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>organizations-module</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>google-analytics-module</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>flow-module</artifactId>
</dependency>

<!-- Spring -->


<!-- CAUSEWAY API -->

<dependency>
<groupId>org.apache.causeway.core</groupId>
<artifactId>causeway-applib</artifactId>
</dependency>


<dependency>
<groupId>org.apache.causeway.valuetypes</groupId>
<artifactId>causeway-valuetypes-markdown-applib</artifactId>
</dependency>

<dependency>
<groupId>org.apache.causeway.persistence</groupId>
<artifactId>causeway-persistence-jpa-applib</artifactId>
</dependency>

<dependency>
<groupId>org.apache.causeway.testing</groupId>
<artifactId>causeway-testing-fixtures-applib</artifactId>
</dependency>


<!-- IDE support (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>


<!-- TESTING -->

<dependency>
<groupId>org.apache.causeway.testing</groupId>
<artifactId>causeway-testing-integtestsupport-applib</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.causeway.persistence</groupId>
<artifactId>causeway-persistence-jpa-eclipselink</artifactId>
<scope>test</scope>
</dependency>


</dependencies>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.savantly.nexus.gaform;

import org.apache.causeway.applib.services.factory.FactoryService;
import org.apache.causeway.persistence.jpa.applib.CausewayModulePersistenceJpaApplib;
import org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript;
import org.apache.causeway.testing.fixtures.applib.modules.ModuleWithFixtures;
import org.apache.causeway.testing.fixtures.applib.teardown.jpa.TeardownFixtureJpaAbstract;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.savantly.nexus.gaform.dom.form.Form_SubmissionListener;
import net.savantly.nexus.organizations.dom.organizationSecret.OrganizationSecrets;

@Configuration
@Import({
CausewayModulePersistenceJpaApplib.class,
})
@ComponentScan
@EnableJpaRepositories
@Slf4j
@EntityScan(basePackageClasses = { GoogleAnalyticsFlowModule.class })
@RequiredArgsConstructor
public class GoogleAnalyticsFlowModule implements ModuleWithFixtures {

public static final String NAMESPACE = "nexus.google.analytics.flow";
public static final String SCHEMA = "google_analytics_flow";

@Override
public FixtureScript getTeardownFixture() {
return new TeardownFixtureJpaAbstract() {
@Override
protected void execute(ExecutionContext executionContext) {
// deleteFrom(FranchiseLocation.class);
}
};
}

@Bean
public Form_SubmissionListener gaFormSubmissionListener(FactoryService factoryService,
OrganizationSecrets secrets) {
return new Form_SubmissionListener(factoryService, secrets);
}

@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
var mapper = new ObjectMapper();
mapper.findAndRegisterModules();
return mapper;
}

}
Loading

0 comments on commit 3d3b557

Please sign in to comment.