Skip to content

Commit 74bbbd6

Browse files
author
yang.guo
authored
Merge pull request #136 from FlowCI/feature/api/import_export_yml
Feature/api/import export yml
2 parents 1f16458 + 10e0ddb commit 74bbbd6

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

platform-api/src/main/java/com/flow/platform/api/controller/FlowController.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,26 @@
2727
import com.flow.platform.api.security.WebSecurity;
2828
import com.flow.platform.api.service.GitService;
2929
import com.flow.platform.api.service.node.YmlService;
30+
import com.flow.platform.api.util.CommonUtil;
3031
import com.flow.platform.core.exception.IllegalParameterException;
3132
import com.flow.platform.util.StringUtil;
3233
import com.google.common.base.Strings;
34+
import java.io.IOException;
3335
import java.util.List;
3436
import java.util.Map;
3537
import java.util.Set;
38+
import javax.servlet.http.HttpServletResponse;
3639
import org.springframework.beans.factory.annotation.Autowired;
40+
import org.springframework.core.io.Resource;
3741
import org.springframework.web.bind.annotation.DeleteMapping;
3842
import org.springframework.web.bind.annotation.GetMapping;
3943
import org.springframework.web.bind.annotation.PostMapping;
4044
import org.springframework.web.bind.annotation.RequestBody;
4145
import org.springframework.web.bind.annotation.RequestMapping;
4246
import org.springframework.web.bind.annotation.RequestParam;
47+
import org.springframework.web.bind.annotation.RequestPart;
4348
import org.springframework.web.bind.annotation.RestController;
49+
import org.springframework.web.multipart.MultipartFile;
4450

4551
/**
4652
* @author yh@firim
@@ -416,6 +422,53 @@ public String createFromYml(@RequestBody(required = false) String yml) {
416422
return yml;
417423
}
418424

425+
/**
426+
* @api {post} /flows/:root/yml/upload upload yml
427+
* @apiParam {String} root flow node name to set yml content
428+
* @apiParam file
429+
* @apiGroup Flow Yml
430+
* @apiDescription Create yml for flow,
431+
* the flow name must be matched with flow name defined in yml
432+
*
433+
* @apiSuccessExample {json} Success-Response
434+
*
435+
* yml body
436+
*/
437+
@PostMapping("/{root}/yml/upload")
438+
@WebSecurity(action = Actions.FLOW_CREATE)
439+
public String createFromUploadYml(@RequestPart MultipartFile file) throws IOException {
440+
String yml = CommonUtil.commonsMultipartFileToString(file);
441+
if (Strings.isNullOrEmpty(yml)) {
442+
throw new IllegalParameterException("file is not available");
443+
}
444+
445+
nodeService.createOrUpdateYml(currentNodePath.get(), yml);
446+
return yml;
447+
}
448+
449+
450+
/**
451+
* @api {post} /flows/:root/yml/download download yml
452+
* @apiParam {String} root flow node name to set yml content
453+
* @apiGroup Flow Yml
454+
* @apiDescription download yml for flow,
455+
* the flow name must be matched with flow name defined in yml
456+
*
457+
* @apiSuccessExample {json} Success-Response
458+
*
459+
* yml file
460+
*/
461+
@GetMapping("/{root}/yml/download")
462+
@WebSecurity(action = Actions.FLOW_CREATE)
463+
public Resource downloadFlowYml(HttpServletResponse httpResponse) {
464+
String path = currentNodePath.get();
465+
Node root = nodeService.find(path).root();
466+
httpResponse.setHeader(
467+
"Content-Disposition",
468+
String.format("attachment; filename=%s", ".flow.yml"));
469+
return ymlService.getResource(root);
470+
}
471+
419472
/**
420473
* @api {post} /flows/:root/users/auth
421474
* @apiParam {String} root flow node name
@@ -481,7 +534,7 @@ public List<User> flowAuthUsers(@RequestBody ListParam<String> listParam) {
481534
* }
482535
*/
483536
@PostMapping("/{root}/trigger")
484-
public Node trigger(@RequestBody TriggerParam triggerParam){
537+
public Node trigger(@RequestBody TriggerParam triggerParam) {
485538
String path = currentNodePath.get();
486539
Node flow = nodeService.find(path).root();
487540
envService.save(flow, triggerParam.toEnv(), true);

platform-api/src/main/java/com/flow/platform/api/service/node/YmlService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.flow.platform.api.domain.node.Yml;
2121
import com.flow.platform.api.domain.request.ThreadConfigParam;
2222
import java.util.function.Consumer;
23+
import org.springframework.core.io.Resource;
2324

2425
/**
2526
* @author yang
@@ -48,6 +49,13 @@ public interface YmlService {
4849
*/
4950
Yml get(Node root);
5051

52+
/**
53+
* yml content to resource
54+
* @param root
55+
* @return
56+
*/
57+
Resource getResource(Node root);
58+
5159
/**
5260
* Load yml content from git repo in async and create tree from yml,
5361
* Then call "get" to get yml

platform-api/src/main/java/com/flow/platform/api/service/node/YmlServiceImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.flow.platform.api.service.node;
1818

19+
import com.flow.platform.api.config.AppConfig;
1920
import com.flow.platform.api.dao.YmlDao;
2021
import com.flow.platform.api.domain.credential.Credential;
2122
import com.flow.platform.api.domain.credential.CredentialType;
@@ -36,17 +37,22 @@
3637
import com.flow.platform.core.context.ContextEvent;
3738
import com.flow.platform.core.exception.IllegalParameterException;
3839
import com.flow.platform.core.exception.IllegalStatusException;
40+
import com.flow.platform.core.exception.NotFoundException;
3941
import com.flow.platform.core.util.ThreadUtil;
4042
import com.flow.platform.util.Logger;
4143
import com.flow.platform.util.git.model.GitSource;
4244
import com.google.common.base.Strings;
4345
import com.google.common.cache.Cache;
4446
import com.google.common.cache.CacheBuilder;
47+
import java.io.ByteArrayInputStream;
48+
import java.io.InputStream;
4549
import java.util.Map;
4650
import java.util.concurrent.ExecutionException;
4751
import java.util.concurrent.TimeUnit;
4852
import java.util.function.Consumer;
4953
import org.springframework.beans.factory.annotation.Autowired;
54+
import org.springframework.core.io.InputStreamResource;
55+
import org.springframework.core.io.Resource;
5056
import org.springframework.core.task.TaskRejectedException;
5157
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
5258
import org.springframework.stereotype.Service;
@@ -110,6 +116,21 @@ public Yml get(final Node root) {
110116
return ymlDao.get(root.getPath());
111117
}
112118

119+
@Override
120+
public Resource getResource(Node root) {
121+
122+
Yml yml = ymlDao.get(root.getPath());
123+
String body = yml.getFile();
124+
Resource allResource;
125+
try (InputStream is = new ByteArrayInputStream(body.getBytes(AppConfig.DEFAULT_CHARSET))) {
126+
allResource = new InputStreamResource(is);
127+
} catch (Throwable throwable) {
128+
throw new NotFoundException("yml not found");
129+
}
130+
131+
return allResource;
132+
}
133+
113134
@Override
114135
public Node startLoad(final Node root, final Consumer<Yml> onSuccess, final Consumer<Throwable> onError) {
115136
if (!EnvUtil.hasRequiredEnvKey(root, GitService.REQUIRED_ENVS)) {

platform-api/src/main/java/com/flow/platform/api/util/CommonUtil.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
package com.flow.platform.api.util;
1818

19+
import com.flow.platform.api.config.AppConfig;
20+
import java.io.InputStream;
1921
import java.math.BigInteger;
2022
import java.time.LocalDateTime;
2123
import java.time.format.DateTimeFormatter;
2224
import java.util.UUID;
25+
import org.apache.commons.io.IOUtils;
26+
import org.springframework.web.multipart.MultipartFile;
2327

2428
/**
2529
* @author yh@firim
@@ -39,4 +43,18 @@ public synchronized static BigInteger randomId() {
3943
return new BigInteger(stringBuilder.append(uuid).toString());
4044
}
4145

46+
47+
/**
48+
* read commonsMultipartFile content to String
49+
* @param file
50+
* @return
51+
*/
52+
public static String commonsMultipartFileToString(MultipartFile file) {
53+
try (InputStream is = file.getInputStream()) {
54+
return IOUtils.toString(is, AppConfig.DEFAULT_CHARSET.name());
55+
} catch (Throwable throwable) {
56+
return null;
57+
}
58+
}
59+
4260
}

platform-api/src/test/java/com/flow/platform/api/test/controller/FlowControllerTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.flow.platform.api.test.controller;
1818

19+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
1920
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
2021
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
2122
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
@@ -28,10 +29,12 @@
2829
import com.flow.platform.api.util.PathUtil;
2930
import com.flow.platform.core.response.ResponseError;
3031
import com.flow.platform.util.StringUtil;
32+
import java.io.IOException;
3133
import org.junit.Assert;
3234
import org.junit.Before;
3335
import org.junit.Test;
3436
import org.springframework.http.MediaType;
37+
import org.springframework.mock.web.MockMultipartFile;
3538
import org.springframework.test.web.servlet.MvcResult;
3639
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
3740

@@ -167,4 +170,37 @@ public void should_return_empty_string_if_no_yml_content() throws Throwable {
167170
// then:
168171
Assert.assertEquals(StringUtil.EMPTY, content);
169172
}
173+
174+
@Test
175+
public void should_upload_yml_success() throws Exception {
176+
String url = "/flows/" + flowName + "/yml/upload";
177+
performRequestWith200Status(fileUpload(url)
178+
.file(createYmlFilePart(".flow.yml"))
179+
);
180+
181+
MockHttpServletRequestBuilder request = get("/flows/" + flowName)
182+
.contentType(MediaType.APPLICATION_JSON);
183+
184+
MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andReturn();
185+
Node flowNode = Node.parse(result.getResponse().getContentAsString(), Node.class);
186+
Assert.assertEquals("FOUND", flowNode.getEnv(FlowEnvs.FLOW_YML_STATUS));
187+
}
188+
189+
@Test
190+
public void should_download_yml_success() throws Exception {
191+
String url = "/flows/" + flowName + "/yml/upload";
192+
performRequestWith200Status(fileUpload(url)
193+
.file(createYmlFilePart(".flow.yml"))
194+
);
195+
196+
MockHttpServletRequestBuilder request = get("/flows/" + flowName + "/yml/download")
197+
.contentType(MediaType.ALL);
198+
MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andReturn();
199+
Assert.assertNotNull(result.getResponse());
200+
}
201+
202+
private MockMultipartFile createYmlFilePart(String name) throws IOException {
203+
String resourceContent = getResourceContent("demo_flow.yaml");
204+
return new MockMultipartFile("file", name, "", resourceContent.getBytes());
205+
}
170206
}

0 commit comments

Comments
 (0)