chore: Add separate request/response views (#31640)

This PR gets finer control into what fields are allowed in
request-body-only, vs what's allowed in response-body-only. This leaves
the fields to separately controlled regarding what can go into the
database and what can't.

[Slack
thread](https://theappsmith.slack.com/archives/CPQNLFHTN/p1710125307810949).
This commit is contained in:
Shrikant Sharat Kandula 2024-03-13 14:50:03 +05:30 committed by GitHub
parent 863214785a
commit 4cdbe89586
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 28 additions and 30 deletions

View File

@ -16,9 +16,9 @@ import com.appsmith.external.models.Executable;
import com.appsmith.external.models.PluginType;
import com.appsmith.external.models.Policy;
import com.appsmith.external.models.Property;
import com.appsmith.external.views.ResponseOnly;
import com.appsmith.external.views.Views;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Getter;
import lombok.NoArgsConstructor;
@ -88,9 +88,8 @@ public class ActionCE_DTO implements Identifiable, Executable {
ActionConfiguration actionConfiguration;
// this attribute carries error messages while processing the actionCollection
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Transient
@JsonView(Views.Public.class)
@JsonView(ResponseOnly.class)
List<ErrorDTO> errorReports;
@JsonView(Views.Public.class)
@ -106,23 +105,19 @@ public class ActionCE_DTO implements Identifiable, Executable {
@JsonView(Views.Public.class)
List<Property> dynamicBindingPathList;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonView(Views.Public.class)
@JsonView(ResponseOnly.class)
Boolean isValid;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonView(Views.Public.class)
@JsonView(ResponseOnly.class)
Set<String> invalids;
@Transient
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonView(Views.Public.class)
@JsonView(ResponseOnly.class)
Set<String> messages = new HashSet<>();
// This is a list of keys that the client whose values the client needs to send during action execution.
// These are the Mustache keys that the server will replace before invoking the API
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonView(Views.Public.class)
@JsonView(ResponseOnly.class)
Set<String> jsonPathKeys;
@JsonView(Views.Internal.class)

View File

@ -0,0 +1,7 @@
package com.appsmith.external.views;
/**
* Intended to annotate fields that can be set by HTTP request payloads, but should NOT be included
* in HTTP responses sent back to the client.
*/
public interface RequestOnly extends Views.Public {}

View File

@ -0,0 +1,8 @@
package com.appsmith.external.views;
/**
* Intended to mark entity/DTO fields that should be included as part of HTTP responses, but should
* be ignored as part of HTTP requests. For example, if a field is marked with this annotation, in
* a class used with {@code @RequestBody}, it's value will NOT be deserialized.
*/
public interface ResponseOnly extends Views.Public {}

View File

@ -6,23 +6,19 @@ import com.appsmith.server.newactions.base.NewActionService;
import com.appsmith.server.refactors.applications.RefactoringService;
import com.appsmith.server.services.LayoutActionService;
import com.appsmith.server.solutions.ActionExecutionSolution;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(Url.ACTION_URL)
@Slf4j
public class ActionController extends ActionControllerCE {
public ActionController(
LayoutActionService layoutActionService,
NewActionService newActionService,
RefactoringService refactoringService,
ActionExecutionSolution actionExecutionSolution,
ObservationRegistry observationRegistry) {
ActionExecutionSolution actionExecutionSolution) {
super(layoutActionService, newActionService, refactoringService, actionExecutionSolution, observationRegistry);
super(layoutActionService, newActionService, refactoringService, actionExecutionSolution);
}
}

View File

@ -2,6 +2,7 @@ package com.appsmith.server.controllers.ce;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.views.RequestOnly;
import com.appsmith.external.views.Views;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.Url;
@ -16,7 +17,6 @@ import com.appsmith.server.refactors.applications.RefactoringService;
import com.appsmith.server.services.LayoutActionService;
import com.appsmith.server.solutions.ActionExecutionSolution;
import com.fasterxml.jackson.annotation.JsonView;
import io.micrometer.observation.ObservationRegistry;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -48,30 +48,25 @@ public class ActionControllerCE {
private final NewActionService newActionService;
private final RefactoringService refactoringService;
private final ActionExecutionSolution actionExecutionSolution;
private final ObservationRegistry observationRegistry;
@Autowired
public ActionControllerCE(
LayoutActionService layoutActionService,
NewActionService newActionService,
RefactoringService refactoringService,
ActionExecutionSolution actionExecutionSolution,
ObservationRegistry observationRegistry) {
ActionExecutionSolution actionExecutionSolution) {
this.layoutActionService = layoutActionService;
this.newActionService = newActionService;
this.refactoringService = refactoringService;
this.actionExecutionSolution = actionExecutionSolution;
this.observationRegistry = observationRegistry;
}
@JsonView(Views.Public.class)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<ActionDTO>> createAction(
@Valid @RequestBody ActionDTO resource,
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName,
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
@Valid @RequestBody @JsonView(RequestOnly.class) ActionDTO resource,
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
log.debug("Going to create resource {}", resource.getClass().getName());
return layoutActionService
.createSingleActionWithBranch(resource, branchName)
@ -82,7 +77,7 @@ public class ActionControllerCE {
@PutMapping("/{defaultActionId}")
public Mono<ResponseDTO<ActionDTO>> updateAction(
@PathVariable String defaultActionId,
@Valid @RequestBody ActionDTO resource,
@Valid @RequestBody @JsonView(RequestOnly.class) ActionDTO resource,
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
log.debug("Going to update resource with defaultActionId: {}, branch: {}", defaultActionId, branchName);
return layoutActionService
@ -178,9 +173,6 @@ public class ActionControllerCE {
* <p>
* The controller function is primarily used with param applicationId by the client to fetch the actions in edit
* mode.
*
* @param params
* @return
*/
@JsonView(Views.Public.class)
@GetMapping("")