Skip to content

Commit

Permalink
Merge pull request #217 from buerokratt/164-165-216-http-put-delete-p…
Browse files Browse the repository at this point in the history
…arams

164 165 216 Support for HTTP PUT/DELETE and parameters in URLs

Self-merged, as currently no-one online to review.
  • Loading branch information
RayDNoper authored Sep 28, 2023
2 parents e051707 + 78ac673 commit 3da2765
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 86 deletions.
4 changes: 0 additions & 4 deletions .env

This file was deleted.

86 changes: 71 additions & 15 deletions .github/workflows/ci-build-publish-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,85 @@ name: Build and publish Ruuter
on:
push:
branches: [ main ]
paths-ignore:
- '**.md'
paths:
- '.env'

jobs:
PackageDeploy:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2

- name: Docker Setup BuildX
uses: docker/setup-buildx-action@v2
- name: Docker Setup BuildX
uses: docker/setup-buildx-action@v2

- name: Set image tag
run: |
LOWER_CASE_GITHUB_REPOSITORY=$(echo $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]')
echo "DOCKER_TAG_CUSTOM=ghcr.io/${LOWER_CASE_GITHUB_REPOSITORY}:${m-beta-2.0.$(date +'%y%m%d%H%M%S')}" >> $GITHUB_ENV
- name: Docker Build
run: docker image build --tag $DOCKER_TAG_CUSTOM .
- name: Load environment variables and set them
run: |
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
echo "RELEASE=$RELEASE" >> $GITHUB_ENV
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "BUILD=$BUILD" >> $GITHUB_ENV
echo "FIX=$FIX" >> $GITHUB_ENV
- name: Set repo
run: |
LOWER_CASE_GITHUB_REPOSITORY=$(echo $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]')
echo "DOCKER_TAG_CUSTOM=ghcr.io/${LOWER_CASE_GITHUB_REPOSITORY}:$RELEASE-$VERSION.$BUILD.$FIX" >> $GITHUB_ENV
echo "$GITHUB_ENV"
- name: Docker Build
run: docker image build --tag $DOCKER_TAG_CUSTOM .

- name: Log in to GitHub container registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin

- name: Log in to GitHub container registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
- name: Push Docker image to ghcr
run: docker push $DOCKER_TAG_CUSTOM

- name: Check if changelog.md exists
id: check_file
run: |
if [ -f changelog.md ]; then
echo "::set-output name=file_exists::true"
else
echo "::set-output name=file_exists::false"
fi
- name: Generate changelog.md
run: |
if [ "${{ steps.check_file.outputs.file_exists }}" = "false" ]; then
echo "## Changelog" >> changelog.md
echo "" >> image_update.md
echo "The Docker image has been successfully built and pushed." >> changelog.md
echo "" >> image_update.md
echo "You can pull the updated image using the following command:" >> changelog.md
echo "" >> changelog.md
# echo "```" >> changelog.md
echo "<code>docker pull $DOCKER_TAG_CUSTOM</code>" >> changelog.md
# echo "```" >> changelog.md
echo "" >> changelog.md
echo "Enjoy the Ruuter!" >> changelog.md
else
> changelog.md
echo "## Docker Image Update" >> changelog.md
echo "" >> image_update.md
echo "The Docker image has been successfully built and pushed." >> changelog.md
echo "" >> changelog.md
echo "You can pull the updated image using the following command:" >> changelog.md
echo "" >> changelog.md
# echo "```" >> changelog.md
echo "<code>docker pull $DOCKER_TAG_CUSTOM</code>" >> changelog.md
# echo "```" >> changelog.md
echo "" >> changelog.md
echo "Enjoy the Ruuter!" >> changelog.md
fi
- name: Push Docker image to ghcr
run: docker push $DOCKER_TAG_CUSTOM
- name: Commit and push updated changelog.md
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
git add image_update.md
git commit -m "Update image_update.md with Docker image info"
git push
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ee.buerokratt.ruuter.domain.steps.http;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpMethod;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class HttpDeleteStep extends HttpPostStep{

@Override
public String getType() {
return "http.delete";
}

@Override
public HttpMethod getMethod() {
return HttpMethod.DELETE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import java.util.Map;
Expand All @@ -17,10 +18,11 @@ public class HttpGetStep extends HttpStep {

@Override
public ResponseEntity<Object> getRequestResponse(DslInstance di) {
String evaluatedURL = di.getScriptingHelper().evaluateScripts(args.getUrl(),di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders()).toString();
Map<String, Object> evaluatedQuery = di.getScriptingHelper().evaluateScripts(args.getQuery(), di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders());
Map<String, Object> evaluatedHeaders = di.getScriptingHelper().evaluateScripts(args.getHeaders(), di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders());
Map<String, String> mappedHeaders = di.getMappingHelper().convertMapObjectValuesToString(evaluatedHeaders);
return di.getHttpHelper().doGet(args.getUrl(), evaluatedQuery, mappedHeaders);
return di.getHttpHelper().doMethod(HttpMethod.GET, evaluatedURL, evaluatedQuery, null, mappedHeaders, null, null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import java.util.Map;
Expand All @@ -20,20 +21,24 @@ protected ResponseEntity<Object> getRequestResponse(DslInstance di) {
Map<String, Object> defaultHeaders = di.getProperties().getHttpPost().getHeaders();
if (defaultHeaders != null && !defaultHeaders.isEmpty())
args.addHeaders(di.getProperties().getHttpPost().getHeaders());
String evaluatedURL = di.getScriptingHelper().evaluateScripts(args.getUrl(),di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders()).toString();
Map<String, Object> evaluatedBody = di.getScriptingHelper().evaluateScripts(args.getBody(), di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders());
Map<String, Object> evaluatedQuery = di.getScriptingHelper().evaluateScripts(args.getQuery(), di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders());
Map<String, Object> evaluatedHeaders = di.getScriptingHelper().evaluateScripts(args.getHeaders(), di.getContext(), di.getRequestBody(), di.getRequestQuery(), di.getRequestHeaders());
Map<String, String> mappedHeaders = di.getMappingHelper().convertMapObjectValuesToString(evaluatedHeaders);

if ("plaintext".equals(args.getContentType()))
return di.getHttpHelper().doPostPlaintext(args.getUrl(), evaluatedBody, evaluatedQuery, mappedHeaders, args.getPlaintext());
else
return di.getHttpHelper().doPost(args.getUrl(), evaluatedBody, evaluatedQuery, mappedHeaders);

return di.getHttpHelper().doMethod(getMethod(), evaluatedURL,
evaluatedQuery, evaluatedBody, mappedHeaders,
"plaintext".equals(args.getContentType()) ? "plaintext" : null,
"plaintext".equals(args.getContentType()) ? args.getPlaintext() : null);
}

@Override
public String getType() {
return "http.post";
}

public HttpMethod getMethod() {
return HttpMethod.POST;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ee.buerokratt.ruuter.domain.steps.http;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpMethod;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class HttpPutStep extends HttpPostStep {
@Override
public String getType() {
return "http.put";
}

@Override
public HttpMethod getMethod() {
return HttpMethod.PUT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
@JsonSubTypes({
@JsonSubTypes.Type(value = HttpGetStep.class, name = "http.get"),
@JsonSubTypes.Type(value = HttpPostStep.class, name = "http.post"),
@JsonSubTypes.Type(value = HttpPutStep.class, name = "http.put"),
@JsonSubTypes.Type(value = HttpDeleteStep.class, name = "http.delete"),
})
@NoArgsConstructor
public abstract class HttpStep extends DslStep {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public ResponseEntity<Object> forwardRequest(String dsl, Map<String, Object> req
.toUriString();

if (methodType.equals(HttpMethod.POST.name())) {
return httpHelper.doPost(forwardingUrl, body, query, headers, contentType);
return httpHelper.doMethod(HttpMethod.POST,forwardingUrl, query, body, headers, contentType, null);
}
if (methodType.equals(HttpMethod.GET.name())) {
return httpHelper.doGet(forwardingUrl, query, headers);
return httpHelper.doMethod(HttpMethod.GET, forwardingUrl, query,null, headers, null, null);
}
throw new InvalidHttpMethodTypeException(methodType);
}
Expand Down
84 changes: 33 additions & 51 deletions src/main/java/ee/buerokratt/ruuter/helper/HttpHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;

import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
Expand All @@ -34,72 +33,55 @@ public ResponseEntity<Object> doPost(String url, Map<String, Object> body, Map<S
return doPost(url, body, query, headers, this.getClass().getName());
}
public ResponseEntity<Object> doPost(String url, Map<String, Object> body, Map<String, Object> query, Map<String, String> headers, String contentType) {

WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(getHttpClient()))
.baseUrl(url)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(query)
.build();

try {
return client.post()
.headers(httpHeaders -> addHeadersIfNotNull(headers, httpHeaders))
.bodyValue(body)
.retrieve()
.toEntity(Object.class)
.block();
} catch (WebClientResponseException e) {
return new ResponseEntity<>(e.getStatusText(), e.getStatusCode());
}
return doMethod(HttpMethod.POST, url, query, body,headers, contentType, null);
}

public ResponseEntity<Object> doPostPlaintext(String url, Map<String, Object> body, Map<String, Object> query, Map<String, String> headers, String plaintext) {
WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(getHttpClient()))
.baseUrl(url)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE)
.defaultUriVariables(query)
.build();

BodyInserter sendable;
if (body.isEmpty()) {
sendable = BodyInserters.fromPublisher(Mono.just(plaintext), String.class);
}
else {
MultiValueMap<String, String> multibody = new LinkedMultiValueMap<>();
body.forEach((s, o) -> multibody.add(s, (String) o));
sendable = BodyInserters.fromFormData(multibody);
}
return doMethod(HttpMethod.POST, url, body, query, headers, "plaintext", plaintext);
}

try {
return client.post()
.headers(httpHeaders -> addHeadersIfNotNull(headers, httpHeaders))
.body(sendable)
.retrieve()
.toEntity(Object.class)
.block();
public ResponseEntity<Object> doGet(String url, Map<String, Object> query, Map<String, String> headers) {
return doMethod(HttpMethod.GET, url, query, null, headers, null, null);
}

} catch (WebClientResponseException e) {
return new ResponseEntity<>(e.getStatusText(), e.getStatusCode());
}
public ResponseEntity<Object> doPut(String url, Map<String, Object> body, Map<String, Object> query, Map<String, String> headers, String contentType) {
return doMethod(HttpMethod.PUT, url, query, body,headers, contentType, null);
}

public ResponseEntity<Object> doDelete(String url, Map<String, Object> body, Map<String, Object> query, Map<String, String> headers, String contentType) {
return doMethod(HttpMethod.DELETE, url, query, body,headers, contentType, null);
}

public ResponseEntity<Object> doGet(String url, Map<String, Object> query, Map<String, String> headers) {
public ResponseEntity<Object> doMethod(HttpMethod method,
String url,
Map<String, Object> query,
Map<String, Object> body,
Map<String, String> headers,
String contentType,
String plaintextValue) {
try {
MultiValueMap<String, String> qp = new LinkedMultiValueMap<>(
query.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e-> Arrays.asList(e.getValue().toString()))));

Object bodyValue;
if (method == HttpMethod.POST &&
"plaintext".equals(contentType) && plaintextValue != null)
bodyValue = plaintextValue;
else if (body == null)
bodyValue = new HashMap<>();
else
bodyValue = body;

return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(getHttpClient())).build()
.get()
.method(method)
.uri(url, uriBuilder -> uriBuilder.queryParams(qp).build())
.bodyValue(bodyValue)
.headers(httpHeaders -> addHeadersIfNotNull(headers, httpHeaders))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toEntity(Object.class)
.block();

} catch (WebClientResponseException e) {
return new ResponseEntity<>(e.getStatusText(), e.getStatusCode());
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/ee/buerokratt/ruuter/service/DslService.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ private boolean allowedToExecuteDSLFrom(DslInstance dsl, String origin, String r
}

private Map<String, DslStep> getGuard(String method, String dslPath) {
if (dslPath.length()<=1 || dslPath.lastIndexOf('/') < 0)
if (dslPath.length()<=1)
return null;
String path = dslPath.substring(0, dslPath.lastIndexOf('/'));
String path = dslPath.contains("/") ? dslPath.substring(0, dslPath.lastIndexOf('/')) : "";
return guards.get(method).containsKey(path) ? guards.get(method).get(path) : getGuard(method, path);
}
}
3 changes: 2 additions & 1 deletion src/main/java/ee/buerokratt/ruuter/util/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public static String getFileNameWithPathWithoutSuffix(Path path) {

public static String getGuardWithPath(Path path) {
String fullPath = getFileNameWithPathWithoutSuffix(path);
return fullPath.substring(0, fullPath.lastIndexOf("/"));
String guard = fullPath.contains("/") ? fullPath.substring(0, fullPath.lastIndexOf("/")) : fullPath;
return guard;
}

public static Map<String, Map<String, String>> parseIniFile(File fileToParse) throws IOException {
Expand Down
Loading

0 comments on commit 3da2765

Please sign in to comment.