Skip to content

Commit f4c7f9c

Browse files
committed
fetch and update flow yaml
1 parent e6c5c54 commit f4c7f9c

File tree

5 files changed

+123
-26
lines changed

5 files changed

+123
-26
lines changed

src/main/java/com/flowci/common/config/AppConfig.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
1717
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
1818

19+
import java.nio.charset.StandardCharsets;
1920
import java.util.List;
2021

2122
@Configuration
@@ -42,12 +43,15 @@ public void addCorsMappings(CorsRegistry registry) {
4243
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
4344
converters.clear();
4445

46+
var jacksonConverter = new MappingJackson2HttpMessageConverter(ObjectMapperFactory.instance());
47+
jacksonConverter.setDefaultCharset(StandardCharsets.UTF_8);
48+
4549
converters.addAll(List.of(
4650
new ByteArrayHttpMessageConverter(),
47-
new MappingJackson2HttpMessageConverter(ObjectMapperFactory.instance()),
51+
jacksonConverter,
4852
new ResourceHttpMessageConverter(),
4953
new AllEncompassingFormHttpMessageConverter(),
50-
new StringHttpMessageConverter()
54+
new StringHttpMessageConverter(StandardCharsets.UTF_8)
5155
));
5256
}
5357
};

src/main/java/com/flowci/flow/FlowController.java

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@
33
import com.flowci.common.exception.DuplicateException;
44
import com.flowci.common.exception.ExceptionUtils;
55
import com.flowci.common.validator.ValidId;
6-
import com.flowci.flow.business.CreateFlow;
7-
import com.flowci.flow.business.FetchFlow;
8-
import com.flowci.flow.business.FetchTemplates;
9-
import com.flowci.flow.business.ListFlows;
6+
import com.flowci.flow.business.*;
107
import com.flowci.flow.model.CreateFlowParam;
118
import com.flowci.flow.model.Flow;
129
import com.flowci.flow.model.YamlTemplate;
10+
import io.swagger.v3.oas.annotations.Operation;
11+
import io.swagger.v3.oas.annotations.Parameter;
12+
import io.swagger.v3.oas.annotations.media.Content;
13+
import io.swagger.v3.oas.annotations.media.ExampleObject;
14+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1315
import io.swagger.v3.oas.annotations.tags.Tag;
1416
import jakarta.validation.Valid;
1517
import jakarta.validation.constraints.Min;
1618
import lombok.AllArgsConstructor;
1719
import lombok.extern.slf4j.Slf4j;
1820
import org.springframework.data.domain.PageRequest;
21+
import org.springframework.http.MediaType;
1922
import org.springframework.web.bind.annotation.*;
2023

2124
import java.util.List;
@@ -30,12 +33,11 @@
3033
public class FlowController {
3134

3235
private final FetchTemplates fetchTemplates;
33-
3436
private final CreateFlow createFlow;
35-
3637
private final ListFlows listFlows;
37-
3838
private final FetchFlow fetchFlow;
39+
private final FetchFlowYamlContent fetchFlowYamlContent;
40+
private final UpdateFlowYamlContent updateFlowYamlContent;
3941

4042
@GetMapping("/{id}")
4143
public Flow getFlow(@PathVariable("id") @Valid @ValidId String id) {
@@ -44,24 +46,19 @@ public Flow getFlow(@PathVariable("id") @Valid @ValidId String id) {
4446

4547
@GetMapping
4648
public List<Flow> getFlows(@RequestParam(required = false, name = "parentId", defaultValue = "10000")
47-
@Valid
48-
@ValidId String parentId,
49+
@Valid @ValidId
50+
String parentId,
4951

5052
@RequestParam(required = false, name = "page", defaultValue = "0")
51-
@Valid
52-
@Min(0) Integer page,
53+
@Valid @Min(0)
54+
Integer page,
5355

5456
@RequestParam(required = false, name = "size", defaultValue = "20")
55-
@Valid
56-
@Min(20) Integer size) {
57+
@Valid @Min(20)
58+
Integer size) {
5759
return listFlows.invoke(parseLong(parentId), PageRequest.of(page, size));
5860
}
5961

60-
@GetMapping("/templates")
61-
public List<YamlTemplate> getTemplates() {
62-
return fetchTemplates.invoke();
63-
}
64-
6562
@PostMapping
6663
public Flow createFlow(@RequestBody @Valid CreateFlowParam param) {
6764
try {
@@ -73,4 +70,45 @@ public Flow createFlow(@RequestBody @Valid CreateFlowParam param) {
7370
);
7471
}
7572
}
73+
74+
@GetMapping("/templates")
75+
public List<YamlTemplate> getTemplates() {
76+
return fetchTemplates.invoke();
77+
}
78+
79+
@Operation(
80+
description = "fetch flow yaml return base64 encoded yaml content",
81+
parameters = @Parameter(name = "id", description = "flow id"),
82+
responses = @ApiResponse(
83+
description = "base64 encoded yaml",
84+
content = @Content(
85+
examples = @ExampleObject(value = "c3RlcHM6CiAgLSBuYW1lOiBzdGVwXzE" +
86+
"KICAgIGNvbW1hbmRzOgogICAgICAtIG5hbWU6IHByaW50CiAgICAgICAgYmFz" +
87+
"aDogfAogICAgICAgICAgZWNobyAic3RlcCAxIG9uIGJhc2gi")
88+
)
89+
)
90+
)
91+
@GetMapping(value = "/{id}/yaml", produces = MediaType.TEXT_PLAIN_VALUE)
92+
public String getYaml(@PathVariable("id") @Valid @ValidId String id) {
93+
return fetchFlowYamlContent.invoke(parseLong(id));
94+
}
95+
96+
@Operation(
97+
description = "update yaml by flow id",
98+
parameters = @Parameter(name = "id", description = "flow id"),
99+
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
100+
description = "base64 encoded yaml",
101+
required = true,
102+
content = @Content(
103+
examples = @ExampleObject(value = "c3RlcHM6CiAgLSBuYW1lOiBzdGVwXzE" +
104+
"KICAgIGNvbW1hbmRzOgogICAgICAtIG5hbWU6IHByaW50CiAgICAgICAgYmFz" +
105+
"aDogfAogICAgICAgICAgZWNobyAic3RlcCAxIG9uIGJhc2gi")
106+
)
107+
)
108+
)
109+
@PostMapping(value = "/{id}/yaml", consumes = MediaType.TEXT_PLAIN_VALUE)
110+
public void updateYaml(@PathVariable("id") @Valid @ValidId String id,
111+
@RequestBody String b64Yaml) {
112+
updateFlowYamlContent.invoke(parseLong(id), b64Yaml);
113+
}
76114
}

src/main/java/com/flowci/flow/business/impl/CreateFlowImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ private Flow toObject(CreateFlowParam param) {
4646
flow.setName(param.name());
4747
flow.setType(Flow.Type.FLOW);
4848
flow.setVariables(Variables.EMPTY);
49-
flow.setParentId(param.rootId() == null ? Flow.ROOT_ID : param.rootId());
49+
flow.setParentId(param.parent() == null ? Flow.ROOT_ID : param.parent());
5050
flow.setCreatedBy(requestContextHolder.getUserId());
5151
flow.setUpdatedBy(requestContextHolder.getUserId());
5252
return flow;

src/main/java/com/flowci/flow/model/CreateFlowParam.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
package com.flowci.flow.model;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.flowci.common.validator.ValidName;
5+
import io.swagger.v3.oas.annotations.media.Schema;
46
import jakarta.annotation.Nullable;
57

6-
public record CreateFlowParam(@ValidName(message = "invalid flow name") String name,
7-
@Nullable String template,
8-
@Nullable Long rootId) {
8+
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;
9+
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
10+
11+
public record CreateFlowParam(@ValidName(message = "invalid flow name")
12+
@Schema(description = "flow name", example = "hello_world", requiredMode = REQUIRED)
13+
String name,
14+
15+
@Nullable
16+
@Schema(description = "template name", example = "maven", requiredMode = NOT_REQUIRED)
17+
String template,
18+
19+
@Nullable
20+
@Schema(description = "parent flow id", example = "110011", requiredMode = NOT_REQUIRED)
21+
Long parent) {
922

1023
private static final String BLANK_TEMPLATE = "_blank_";
1124

25+
@JsonIgnore
1226
public boolean isBlank() {
1327
return template == null || BLANK_TEMPLATE.equals(template);
1428
}

src/test/java/com/flowci/flow/FlowControllerTest.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,28 @@
55
import com.flowci.common.model.ErrorResponse;
66
import com.flowci.flow.business.CreateFlow;
77
import com.flowci.flow.business.FetchFlow;
8+
import com.flowci.flow.business.FetchFlowYamlContent;
9+
import com.flowci.flow.business.UpdateFlowYamlContent;
810
import com.flowci.flow.model.CreateFlowParam;
911
import com.flowci.flow.model.Flow;
1012
import org.junit.jupiter.api.Test;
13+
import org.mockito.ArgumentCaptor;
1114
import org.springframework.beans.factory.annotation.Autowired;
1215
import org.springframework.boot.test.mock.mockito.MockBean;
1316
import org.springframework.dao.DataIntegrityViolationException;
1417
import org.springframework.http.MediaType;
1518
import org.springframework.test.web.servlet.MockMvc;
1619

1720
import java.sql.SQLException;
21+
import java.util.Base64;
1822

1923
import static com.flowci.TestUtils.newDummyInstance;
2024
import static org.junit.jupiter.api.Assertions.assertEquals;
2125
import static org.mockito.ArgumentMatchers.any;
22-
import static org.mockito.Mockito.mock;
23-
import static org.mockito.Mockito.when;
26+
import static org.mockito.Mockito.*;
2427
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
2528
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
29+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
2630
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
2731

2832
class FlowControllerTest extends SpringTest {
@@ -39,6 +43,12 @@ class FlowControllerTest extends SpringTest {
3943
@MockBean
4044
private FetchFlow fetchFlow;
4145

46+
@MockBean
47+
private FetchFlowYamlContent fetchFlowYamlContent;
48+
49+
@MockBean
50+
private UpdateFlowYamlContent updateFlowYamlContent;
51+
4252
@Test
4353
void givenCreateFlowParameter_whenCreateFlow_thenReturnFlowId() throws Exception {
4454
var expectedFlow = new Flow();
@@ -135,4 +145,35 @@ void givenInvalidFlowId_whenFetching_thenReturnError() throws Exception {
135145
assertEquals(400, error.code());
136146
assertEquals("invalid id", error.message());
137147
}
148+
149+
@Test
150+
void givenFlowId_whenFetchingYaml_thenReturnBase64EncodedYaml() throws Exception {
151+
var expectedYaml = Base64.getEncoder().encodeToString("some yaml".getBytes());
152+
when(fetchFlowYamlContent.invoke(any())).thenReturn(expectedYaml);
153+
154+
var r = mvc.perform(get("/v2/flows/1001/yaml"))
155+
.andExpectAll(
156+
status().is2xxSuccessful(),
157+
header().string("Content-Type", "text/plain;charset=UTF-8")
158+
)
159+
.andReturn();
160+
161+
assertEquals(expectedYaml, r.getResponse().getContentAsString());
162+
}
163+
164+
@Test
165+
void givenFlowIdAndYaml_whenUpdating_thenYamlIsUpdated() throws Exception {
166+
var expectedYaml = Base64.getEncoder().encodeToString("some yaml".getBytes());
167+
168+
var yamlCaptor = ArgumentCaptor.forClass(String.class);
169+
doNothing().when(updateFlowYamlContent).invoke(any(), yamlCaptor.capture());
170+
171+
var r = mvc.perform(post("/v2/flows/1001/yaml")
172+
.contentType(MediaType.TEXT_PLAIN)
173+
.content(expectedYaml))
174+
.andExpect(status().is2xxSuccessful())
175+
.andReturn();
176+
177+
assertEquals(expectedYaml, yamlCaptor.getValue());
178+
}
138179
}

0 commit comments

Comments
 (0)