Skip to content

Commit

Permalink
Dynamic qualifiers (#871)
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-v authored Dec 23, 2024
1 parent d704ba4 commit b0cc785
Show file tree
Hide file tree
Showing 23 changed files with 837 additions and 167 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.scalecube.services;

import io.scalecube.services.api.DynamicQualifier;
import io.scalecube.services.api.Qualifier;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -13,12 +14,13 @@
*/
public class ServiceReference {

private final String qualifier;
private final String endpointId;
private final String namespace;
private final String action;
private final String qualifier;
private final DynamicQualifier dynamicQualifier;
private final Set<String> contentTypes;
private final Map<String, String> tags;
private final String action;
private final Address address;
private final boolean isSecured;

Expand All @@ -35,18 +37,15 @@ public ServiceReference(
ServiceEndpoint serviceEndpoint) {
this.endpointId = serviceEndpoint.id();
this.namespace = serviceRegistration.namespace();
this.contentTypes = Collections.unmodifiableSet(serviceEndpoint.contentTypes());
this.tags = mergeTags(serviceMethodDefinition, serviceRegistration, serviceEndpoint);
this.action = serviceMethodDefinition.action();
this.qualifier = Qualifier.asString(namespace, action);
this.dynamicQualifier = qualifier.contains(":") ? new DynamicQualifier(qualifier) : null;
this.contentTypes = Collections.unmodifiableSet(serviceEndpoint.contentTypes());
this.tags = mergeTags(serviceMethodDefinition, serviceRegistration, serviceEndpoint);
this.address = serviceEndpoint.address();
this.isSecured = serviceMethodDefinition.isSecured();
}

public String qualifier() {
return qualifier;
}

public String endpointId() {
return endpointId;
}
Expand All @@ -55,6 +54,18 @@ public String namespace() {
return namespace;
}

public String action() {
return action;
}

public String qualifier() {
return qualifier;
}

public DynamicQualifier dynamicQualifier() {
return dynamicQualifier;
}

public Set<String> contentTypes() {
return contentTypes;
}
Expand All @@ -63,10 +74,6 @@ public Map<String, String> tags() {
return tags;
}

public String action() {
return action;
}

public Address address() {
return this.address;
}
Expand All @@ -89,11 +96,14 @@ private Map<String, String> mergeTags(
@Override
public String toString() {
return new StringJoiner(", ", ServiceReference.class.getSimpleName() + "[", "]")
.add("endpointId=" + endpointId)
.add("address=" + address)
.add("qualifier=" + qualifier)
.add("endpointId='" + endpointId + "'")
.add("namespace='" + namespace + "'")
.add("action='" + action + "'")
.add("qualifier='" + qualifier + "'")
.add("dynamicQualifier=" + dynamicQualifier)
.add("contentTypes=" + contentTypes)
.add("tags=" + tags)
.add("address=" + address)
.add("isSecured=" + isSecured)
.toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package io.scalecube.services.api;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Pattern;

public final class DynamicQualifier {

private final String qualifier;
private final Pattern pattern;
private final List<String> pathVariables;
private final int size;

public DynamicQualifier(String qualifier) {
if (!qualifier.contains(":")) {
throw new IllegalArgumentException("Illegal dynamic qualifier: " + qualifier);
}

final var pathVariables = new ArrayList<String>();
final var sb = new StringBuilder();
for (var s : qualifier.split("/")) {
if (s.startsWith(":")) {
final var pathVar = s.substring(1);
sb.append("(?<").append(pathVar).append(">.*?)");
pathVariables.add(pathVar);
} else {
sb.append(s);
}
sb.append("/");
}
sb.setLength(sb.length() - 1);

this.qualifier = qualifier;
this.pattern = Pattern.compile(sb.toString());
this.pathVariables = Collections.unmodifiableList(pathVariables);
this.size = sizeOf(qualifier);
}

public String qualifier() {
return qualifier;
}

public Pattern pattern() {
return pattern;
}

public List<String> pathVariables() {
return pathVariables;
}

public int size() {
return size;
}

public Map<String, String> matchQualifier(String input) {
if (size != sizeOf(input)) {
return null;
}

final var matcher = pattern.matcher(input);
if (!matcher.matches()) {
return null;
}

final var map = new LinkedHashMap<String, String>();
for (var pathVar : pathVariables) {
final var value = matcher.group(pathVar);
Objects.requireNonNull(
value, "Path variable value must not be null, path variable: " + pathVar);
map.put(pathVar, value);
}

return map;
}

private static int sizeOf(String value) {
int count = 0;
for (int i = 0, length = value.length(); i < length; i++) {
if (value.charAt(i) == '/') {
count++;
}
}
return count;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return Objects.equals(qualifier, ((DynamicQualifier) o).qualifier);
}

@Override
public int hashCode() {
return Objects.hashCode(qualifier);
}

@Override
public String toString() {
return new StringJoiner(", ", DynamicQualifier.class.getSimpleName() + "[", "]")
.add("qualifier='" + qualifier + "'")
.add("pattern=" + pattern)
.add("pathVariables=" + pathVariables)
.add("size=" + size)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.scalecube.services.methods;

import io.scalecube.services.CommunicationMode;
import io.scalecube.services.api.DynamicQualifier;
import io.scalecube.services.api.Qualifier;
import java.lang.reflect.Type;
import java.util.StringJoiner;
Expand All @@ -11,6 +12,7 @@ public final class MethodInfo {
private final String serviceName;
private final String methodName;
private final String qualifier;
private final DynamicQualifier dynamicQualifier;
private final Type parameterizedReturnType;
private final boolean isReturnTypeServiceMessage;
private final CommunicationMode communicationMode;
Expand Down Expand Up @@ -51,6 +53,7 @@ public MethodInfo(
this.serviceName = serviceName;
this.methodName = methodName;
this.qualifier = Qualifier.asString(serviceName, methodName);
this.dynamicQualifier = qualifier.contains(":") ? new DynamicQualifier(qualifier) : null;
this.parameterCount = parameterCount;
this.requestType = requestType;
this.isRequestTypeServiceMessage = isRequestTypeServiceMessage;
Expand All @@ -70,6 +73,10 @@ public String qualifier() {
return qualifier;
}

public DynamicQualifier dynamicQualifier() {
return dynamicQualifier;
}

public Type parameterizedReturnType() {
return parameterizedReturnType;
}
Expand Down Expand Up @@ -112,6 +119,7 @@ public String toString() {
.add("serviceName='" + serviceName + "'")
.add("methodName='" + methodName + "'")
.add("qualifier='" + qualifier + "'")
.add("dynamicQualifier=" + dynamicQualifier)
.add("parameterizedReturnType=" + parameterizedReturnType)
.add("isReturnTypeServiceMessage=" + isReturnTypeServiceMessage)
.add("communicationMode=" + communicationMode)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.scalecube.services.methods;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import reactor.core.publisher.Mono;

public class RequestContext {

private final Map<String, String> headers;
private final Object principal;
private final Map<String, String> pathVars;

/**
* Constructor.
*
* @param headers message headers
* @param principal authenticated principal (optional)
* @param pathVars path variables (optional)
*/
public RequestContext(
Map<String, String> headers, Object principal, Map<String, String> pathVars) {
this.headers = Collections.unmodifiableMap(new HashMap<>(headers));
this.principal = principal;
this.pathVars = pathVars != null ? Map.copyOf(pathVars) : null;
}

public Map<String, String> headers() {
return headers;
}

public String header(String name) {
return headers.get(name);
}

public <T> T principal() {
//noinspection unchecked
return (T) principal;
}

public Map<String, String> pathVars() {
return pathVars;
}

public String pathVar(String name) {
return pathVars != null ? pathVars.get(name) : null;
}

public static Mono<RequestContext> deferContextual() {
return Mono.deferContextual(context -> Mono.just(context.get(RequestContext.class)));
}

@Override
public String toString() {
return new StringJoiner(", ", RequestContext.class.getSimpleName() + "[", "]")
.add("headers=" + headers)
.add("principal=" + principal)
.add("pathVars=" + pathVars)
.toString();
}
}
Loading

0 comments on commit b0cc785

Please sign in to comment.