Skip to content

Commit

Permalink
[WIP] Add HTTP API version 1
Browse files Browse the repository at this point in the history
Motivation:
The libthrift is not stable enough yet, we need two diffrent jars to compile.
If we use HTTP API, we can avoid that situation and have fewer depedencies for the client library.

Modifications:
- Revise admin REST APIs
- Add more APIs to cover all of the features in Thrift service

Result:
- We are ready to transfer from Thrift to HTTP API

Todos:
- Catch more exceptions and respond it with proper status
- Add meaningful reponse body corresponding to the invalid requests
- Gather scattered exception handling
_ Remove admin package or keep it minumun
- Remove thrift service ultimately
  • Loading branch information
minwoox committed Dec 14, 2017
1 parent f573548 commit fbb8090
Show file tree
Hide file tree
Showing 42 changed files with 3,637 additions and 207 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.linecorp.centraldogma.common;

import static com.linecorp.centraldogma.internal.Util.validateFilePath;
import static java.util.Objects.requireNonNull;

import java.util.Collections;
Expand All @@ -31,7 +32,8 @@ final class IdentityQuery implements Query<Object> {

@JsonCreator
IdentityQuery(@JsonProperty("path") String path) {
this.path = requireNonNull(path, "path");
requireNonNull(path, "path");
this.path = validateFilePath(path, "path");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.linecorp.centraldogma.common;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.linecorp.centraldogma.internal.Util.validateJsonFilePath;
import static java.util.Objects.requireNonNull;

import java.util.List;
Expand All @@ -41,7 +42,7 @@ final class JsonPathQuery implements Query<JsonNode> {
requireNonNull(path, "path");
requireNonNull(jsonPaths, "jsonPaths");

this.path = path;
this.path = validateJsonFilePath(path, "path");
this.jsonPaths = Stream.of(jsonPaths).peek(JsonPathQuery::validateJsonPath).collect(toImmutableList());
}

Expand All @@ -52,7 +53,7 @@ final class JsonPathQuery implements Query<JsonNode> {
requireNonNull(path, "path");
requireNonNull(jsonPaths, "jsonPaths");

this.path = path;
this.path = validateJsonFilePath(path, "path");
this.jsonPaths = Streams.stream(jsonPaths)
.peek(JsonPathQuery::validateJsonPath)
.collect(toImmutableList());
Expand Down
26 changes: 26 additions & 0 deletions common/src/main/java/com/linecorp/centraldogma/common/Markup.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package com.linecorp.centraldogma.common;

import static com.google.common.base.Strings.isNullOrEmpty;

import javax.annotation.Nullable;

import com.google.common.base.Ascii;

/**
Expand Down Expand Up @@ -46,4 +50,26 @@ public enum Markup {
public String nameLowercased() {
return nameLowercased;
}

/**
* Returns the {@link Markup} from the specified {@code value}. If none of markup is matched,
* this will return {@link #UNKNOWN}.
*/
public static Markup parse(@Nullable String value) {
if (isNullOrEmpty(value)) {
return UNKNOWN;
}

final String markup = Ascii.toUpperCase(value);

if ("PLAINTEXT".equalsIgnoreCase(markup)) {
return PLAINTEXT;
}

if ("MARKDOWN".equalsIgnoreCase(markup)) {
return MARKDOWN;
}

return UNKNOWN;
}
}
12 changes: 6 additions & 6 deletions common/src/main/java/com/linecorp/centraldogma/common/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
import com.fasterxml.jackson.databind.JsonNode;

/**
* A query on an {@link Entry}.
* A query on a file.
*
* @param <T> the content type of an {@link Entry} being queried
* @param <T> the content type of a file being queried
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
Expand All @@ -44,7 +44,7 @@ public interface Query<T> extends Function<T, T> {
/**
* Returns a newly-created {@link Query} that retrieves the content as it is.
*
* @param path the path of the {@link Entry} being queried on
* @param path the path of the a file being queried on
*/
static Query<Object> identity(String path) {
return new IdentityQuery(path);
Expand All @@ -55,7 +55,7 @@ static Query<Object> identity(String path) {
* <a href="https://github.com/json-path/JsonPath/blob/master/README.md">JSON path expressions</a>
* to the content.
*
* @param path the path of the {@link Entry} being queried on
* @param path the path of a file being queried on
* @param jsonPaths the JSON path expressions to apply
*/
static Query<JsonNode> ofJsonPath(String path, String... jsonPaths) {
Expand All @@ -67,7 +67,7 @@ static Query<JsonNode> ofJsonPath(String path, String... jsonPaths) {
* <a href="https://github.com/json-path/JsonPath/blob/master/README.md">JSON path expressions</a>
* to the content.
*
* @param path the path of the {@link Entry} being queried on
* @param path the path of a file being queried on
* @param jsonPaths the JSON path expressions to apply
*/
static Query<JsonNode> ofJsonPath(String path, Iterable<String> jsonPaths) {
Expand All @@ -78,7 +78,7 @@ static Query<JsonNode> ofJsonPath(String path, Iterable<String> jsonPaths) {
* Returns a newly-created {@link Query} that applies a series of expressions to the content.
*
* @param type the type of the {@link Query}
* @param path the path of the {@link Entry} being queried
* @param path the path of a file being queried on
* @param expressions the expressions to apply
*/
static Query<?> of(QueryType type, String path, @Nullable String... expressions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@

package com.linecorp.centraldogma.common;

import static com.google.common.base.Strings.isNullOrEmpty;

import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;

import javax.annotation.Nullable;

import com.google.common.base.Ascii;

/**
* The type of a {@link Query}.
*/
Expand All @@ -46,4 +52,22 @@ public enum QueryType {
public Set<EntryType> supportedEntryTypes() {
return supportedEntryTypes;
}

/**
* Returns the {@link QueryType} from the specified {@code value}. If none of query type is matched,
* this will return {@link #IDENTITY}.
*/
public static QueryType parse(@Nullable String value) {
if (isNullOrEmpty(value)) {
return IDENTITY;
}

final String queryType = Ascii.toUpperCase(value);

if ("JSON_PATH".equalsIgnoreCase(queryType)) {
return JSON_PATH;
}

return IDENTITY;
}
}
35 changes: 30 additions & 5 deletions common/src/main/java/com/linecorp/centraldogma/internal/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public final class Util {
"^(?:[-_0-9a-zA-Z](?:[-_\\.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+$");
private static final Pattern FILE_PATH_PATTERN = Pattern.compile(
"^(?:/[-_0-9a-zA-Z](?:[-_\\.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+$");
private static final Pattern JSON_FILE_PATH_PATTERN = Pattern.compile(
"^(?:/[-_0-9a-zA-Z](?:[-_\\.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+\\.(?i)json$");
private static final Pattern DIR_PATH_PATTERN = Pattern.compile(
"^(?:/[-_0-9a-zA-Z](?:[-_\\.0-9a-zA-Z]*[-_0-9a-zA-Z])?)*/?$");
private static final Pattern EMAIL_PATTERN = Pattern.compile(
Expand All @@ -55,6 +57,11 @@ public static String validateFileName(String name, String paramName) {
paramName + ": " + name + " (expected: " + FILE_NAME_PATTERN.pattern() + ')');
}

public static boolean isValidFileName(String name) {
requireNonNull(name, "name");
return !name.isEmpty() && FILE_NAME_PATTERN.matcher(name).matches();
}

public static String validateFilePath(String path, String paramName) {
requireNonNull(path, paramName);
if (isValidFilePath(path)) {
Expand All @@ -65,17 +72,28 @@ public static String validateFilePath(String path, String paramName) {
paramName + ": " + path + " (expected: " + FILE_PATH_PATTERN.pattern() + ')');
}

public static boolean isValidFileName(String name) {
requireNonNull(name, "name");
return !name.isEmpty() && FILE_NAME_PATTERN.matcher(name).matches();
}

public static boolean isValidFilePath(String path) {
requireNonNull(path, "path");
return !path.isEmpty() && path.charAt(0) == '/' &&
FILE_PATH_PATTERN.matcher(path).matches();
}

public static String validateJsonFilePath(String path, String paramName) {
requireNonNull(path, paramName);
if (isValidJsonFilePath(path)) {
return path;
}

throw new IllegalArgumentException(
paramName + ": " + path + " (expected: " + JSON_FILE_PATH_PATTERN.pattern() + ')');
}

public static boolean isValidJsonFilePath(String path) {
requireNonNull(path, "path");
return !path.isEmpty() && path.charAt(0) == '/' &&
JSON_FILE_PATH_PATTERN.matcher(path).matches();
}

public static String validateDirPath(String path, String paramName) {
requireNonNull(path, paramName);
if (isValidDirPath(path)) {
Expand All @@ -87,7 +105,14 @@ public static String validateDirPath(String path, String paramName) {
}

public static boolean isValidDirPath(String path) {
return isValidDirPath(path, false);
}

public static boolean isValidDirPath(String path, boolean mustEndsWithSlash) {
requireNonNull(path);
if (mustEndsWithSlash && !path.endsWith("/")) {
return false;
}
return !path.isEmpty() && path.charAt(0) == '/' &&
DIR_PATH_PATTERN.matcher(path).matches();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2017 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.linecorp.centraldogma.internal.httpapi.v1;

import static java.util.Objects.requireNonNull;

import javax.annotation.Nullable;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;

import com.linecorp.centraldogma.common.ChangeType;

@JsonInclude(Include.NON_DEFAULT)
public class ChangeDto<T> {

private final String path;

private final ChangeType type;

@Nullable
private final T content;

@JsonCreator
public ChangeDto(String path, ChangeType type, @Nullable T content) {
this.path = requireNonNull(path, "path");
this.type = requireNonNull(type, "type");
this.content = content;
}

@JsonProperty("path")
public String path() {
return path;
}

@JsonProperty("type")
public ChangeType type() {
return type;
}

@JsonProperty("content")
public T content() {
return content;
}

@Override
public String toString() {
final ToStringHelper stringHelper = MoreObjects.toStringHelper(this)
.add("path", path())
.add("type", type());
if (content() != null) {
stringHelper.add("content", content());
}
return stringHelper.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2017 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.linecorp.centraldogma.internal.httpapi.v1;

import static java.time.format.DateTimeFormatter.ISO_INSTANT;
import static java.util.Objects.requireNonNull;

import java.time.Instant;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;

import com.linecorp.centraldogma.common.Author;
import com.linecorp.centraldogma.common.Commit;
import com.linecorp.centraldogma.common.Revision;

public class CommitDto {

private final Revision revision;

private final Author author;

private final CommitMessageDto commitMessage;

private final String pushedAt;

@JsonCreator
public CommitDto(Commit commit) {
requireNonNull(commit, "commit");
revision = commit.revision();
author = commit.author();
commitMessage = new CommitMessageDto(commit.summary(), commit.detail(), commit.markup());
pushedAt = ISO_INSTANT.format(Instant.ofEpochMilli(commit.when()));
}

@JsonProperty("revision")
public Revision revision() {
return revision;
}

@JsonProperty("author")
public Author author() {
return author;
}

@JsonProperty("pushedAt")
public String pushedAt() {
return pushedAt;
}

@JsonProperty("commitMessage")
public CommitMessageDto commitMessage() {
return commitMessage;
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("revision", revision())
.add("author", author())
.add("commitMessage", commitMessage())
.add("pushedAt", pushedAt())
.toString();
}
}
Loading

0 comments on commit fbb8090

Please sign in to comment.