Merge pull request #12997 from overleaf/em-git-bridge-pat-messaging
Adapt git bridge messaging to personal access tokens GitOrigin-RevId: 0b5d5ecbd97814c8b18827fe11e77513c11bab3a
|
@ -0,0 +1,21 @@
|
|||
package uk.ac.ic.wlgitbridge.server;
|
||||
|
||||
public class BasicAuthCredentials {
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public BasicAuthCredentials(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,9 @@ package uk.ac.ic.wlgitbridge.server;
|
|||
|
||||
import com.google.api.client.auth.oauth2.*;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpHeaders;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import uk.ac.ic.wlgitbridge.application.config.Oauth2;
|
||||
|
@ -43,18 +46,6 @@ public class Oauth2Filter implements Filter {
|
|||
@Override
|
||||
public void init(FilterConfig filterConfig) {}
|
||||
|
||||
private void sendResponse(ServletResponse servletResponse, int code, List<String> lines) throws IOException {
|
||||
HttpServletResponse response = ((HttpServletResponse) servletResponse);
|
||||
response.setContentType("text/plain");
|
||||
response.setStatus(code);
|
||||
PrintWriter w = response.getWriter();
|
||||
for (String line : lines) {
|
||||
w.println(line);
|
||||
}
|
||||
w.close();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original request from git will not contain the Authorization header.
|
||||
*
|
||||
|
@ -68,216 +59,343 @@ public class Oauth2Filter implements Filter {
|
|||
*/
|
||||
@Override
|
||||
public void doFilter(
|
||||
ServletRequest servletRequest,
|
||||
ServletResponse servletResponse,
|
||||
FilterChain filterChain
|
||||
) throws IOException, ServletException {
|
||||
String requestUri = ((Request) servletRequest).getRequestURI();
|
||||
if (requestUri.startsWith("/project")) {
|
||||
Log.info("[{}] Invalid request URI", requestUri);
|
||||
sendResponse(servletResponse,404, Arrays.asList(
|
||||
"Invalid Project ID (must not have a '/project' prefix)"
|
||||
));
|
||||
return;
|
||||
}
|
||||
String project = Util.removeAllSuffixes(
|
||||
requestUri.split("/")[1],
|
||||
".git"
|
||||
);
|
||||
// Reject v1 ids, the request will be rejected by v1 anyway
|
||||
if (project.matches("^[0-9]+[bcdfghjklmnpqrstvwxyz]{6,12}$") && !project.matches("^[0-9a-f]{24}$")) {
|
||||
Log.info("[{}] Request for v1 project, refusing", project);
|
||||
sendResponse(servletResponse, 404, Arrays.asList(
|
||||
"This project has not yet been moved into the new version",
|
||||
"of Overleaf. You will need to move it in order to continue working on it.",
|
||||
"Please visit this project online on www.overleaf.com to do this.",
|
||||
"",
|
||||
"You can find the new git remote url by selecting \"Git\" from",
|
||||
"the left sidebar in the project view.",
|
||||
"",
|
||||
"If this is unexpected, please contact us at support@overleaf.com, or",
|
||||
"see https://www.overleaf.com/help/342 for more information."
|
||||
));
|
||||
return;
|
||||
}
|
||||
Log.debug("[{}] Checking if auth needed", project);
|
||||
GetDocRequest doc = new GetDocRequest(project);
|
||||
doc.request();
|
||||
try {
|
||||
SnapshotApi.getResult(
|
||||
snapshotApi.getDoc(Optional.empty(), project));
|
||||
} catch (ForbiddenException e) {
|
||||
Log.debug("[{}] Auth needed", project);
|
||||
getAndInjectCredentials(
|
||||
project,
|
||||
servletRequest,
|
||||
servletResponse,
|
||||
filterChain
|
||||
);
|
||||
return;
|
||||
} catch (MissingRepositoryException e) {
|
||||
handleMissingRepository(project, e, (HttpServletResponse) servletResponse);
|
||||
}
|
||||
Log.debug("[{}] Auth not needed", project);
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
// TODO: this is ridiculous. Check for error cases first, then return/throw
|
||||
// TODO: also, use an Optional credential, since we treat it as optional
|
||||
private void getAndInjectCredentials(
|
||||
String projectName,
|
||||
ServletRequest servletRequest,
|
||||
ServletResponse servletResponse,
|
||||
FilterChain filterChain
|
||||
ServletRequest servletRequest,
|
||||
ServletResponse servletResponse,
|
||||
FilterChain filterChain
|
||||
) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
String capturedUsername = "(unknown)";
|
||||
String requestUri = request.getRequestURI();
|
||||
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null) {
|
||||
String clientIp = request.getHeader("X-Forwarded-For");
|
||||
if (clientIp == null) {
|
||||
clientIp = request.getRemoteAddr();
|
||||
}
|
||||
Log.debug("[{}] Authorization header present", clientIp);
|
||||
StringTokenizer st = new StringTokenizer(authHeader);
|
||||
if (st.hasMoreTokens()) {
|
||||
String basic = st.nextToken();
|
||||
if (basic.equalsIgnoreCase("Basic")) {
|
||||
try {
|
||||
String credentials = new String(
|
||||
Base64.decodeBase64(st.nextToken()),
|
||||
"UTF-8"
|
||||
);
|
||||
String[] split = credentials.split(":",2);
|
||||
if (split.length == 2) {
|
||||
String username = split[0];
|
||||
String password = split[1];
|
||||
|
||||
final Credential cred = new Credential.Builder(
|
||||
BearerToken.authorizationHeaderAccessMethod()
|
||||
).build();
|
||||
|
||||
if(username.equals("git")) {
|
||||
Log.debug("[{}] username is 'git', skipping password grant flow", projectName);
|
||||
cred.setAccessToken(password);
|
||||
} else if (this.isUserPasswordEnabled) {
|
||||
String accessToken = null;
|
||||
if (username.length() > 0) {
|
||||
capturedUsername = username;
|
||||
}
|
||||
try {
|
||||
accessToken = new PasswordTokenRequest(
|
||||
Instance.httpTransport,
|
||||
Instance.jsonFactory,
|
||||
new GenericUrl(
|
||||
oauth2.getOauth2Server()
|
||||
+ "/oauth/token?client_ip="
|
||||
+ clientIp
|
||||
),
|
||||
username,
|
||||
password
|
||||
).setClientAuthentication(
|
||||
new ClientParametersAuthentication(
|
||||
oauth2.getOauth2ClientID(),
|
||||
oauth2.getOauth2ClientSecret()
|
||||
)
|
||||
).execute().getAccessToken();
|
||||
} catch (TokenResponseException e) {
|
||||
handleNeedAuthorization(projectName, capturedUsername, e.getStatusCode(), request, response);
|
||||
return;
|
||||
}
|
||||
cred.setAccessToken(accessToken);
|
||||
}
|
||||
|
||||
servletRequest.setAttribute(ATTRIBUTE_KEY, cred);
|
||||
filterChain.doFilter(
|
||||
servletRequest,
|
||||
servletResponse
|
||||
);
|
||||
|
||||
} else {
|
||||
handleNeedAuthorization(projectName, capturedUsername, 0, request, response);
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("Couldn't retrieve authentication", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleNeedAuthorization(projectName, capturedUsername, 0, request, response);
|
||||
if (requestUri.startsWith("/project")) {
|
||||
Log.info("[{}] Invalid request URI", requestUri);
|
||||
sendResponse(response,404, Arrays.asList(
|
||||
"Invalid Project ID (must not have a '/project' prefix)"
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
String projectId = Util.removeAllSuffixes(requestUri.split("/")[1], ".git");
|
||||
|
||||
BasicAuthCredentials basicAuthCreds = getBasicAuthCredentials(request);
|
||||
if (basicAuthCreds == null) {
|
||||
handleNeedAuthorization(projectId, "(unknown)", request, response);
|
||||
return;
|
||||
}
|
||||
String username = basicAuthCreds.getUsername();
|
||||
String password = basicAuthCreds.getPassword();
|
||||
|
||||
if (isLinkSharingId(projectId)) {
|
||||
handleLinkSharingId(projectId, username, request, response);
|
||||
return;
|
||||
}
|
||||
if (!isProjectId(projectId)) {
|
||||
handleBadProjectId(projectId, username, request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
final Credential cred = new Credential.Builder(
|
||||
BearerToken.authorizationHeaderAccessMethod()
|
||||
).build();
|
||||
|
||||
if (username.equals("git")) {
|
||||
Log.debug("[{}] username is 'git', skipping password grant flow", projectId);
|
||||
|
||||
// Check that the access token is valid. In principle, we could
|
||||
// wait until we make the actual request to the web api, but the
|
||||
// JGit API doesn't make it easy to reply with a 401 and a custom
|
||||
// error message. This is something we can do in this filter, so as
|
||||
// a workaround, we use the /oauth/token/info endpoint to verify
|
||||
// the access token.
|
||||
//
|
||||
// It's still theoretically possible for the web api request to
|
||||
// fail later (for example, in the unlikely event that the token
|
||||
// expired between the two requests). In that case, JGit will
|
||||
// return a 401 without a custom error message.
|
||||
int statusCode = checkAccessToken(oauth2, password, getClientIp(request));
|
||||
if (statusCode == 429) {
|
||||
handleRateLimit(projectId, username, request, response);
|
||||
return;
|
||||
} else if (statusCode == 401) {
|
||||
handleBadAccessToken(projectId, request, response);
|
||||
return;
|
||||
} else if (statusCode >= 400) {
|
||||
handleUnknownOauthServerError(projectId, statusCode, request, response);
|
||||
return;
|
||||
}
|
||||
cred.setAccessToken(password);
|
||||
} else if (this.isUserPasswordEnabled) {
|
||||
String accessToken = null;
|
||||
try {
|
||||
accessToken = doPasswordGrantFlow(username, password, getClientIp(request));
|
||||
} catch (TokenResponseException e) {
|
||||
int statusCode = e.getStatusCode();
|
||||
if (statusCode == 429) {
|
||||
handleRateLimit(projectId, username, request, response);
|
||||
} else if (statusCode == 400 || statusCode == 401) {
|
||||
handleNeedAuthorization(projectId, username, request, response);
|
||||
} else {
|
||||
handleUnknownOauthServerError(projectId, statusCode, request, response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
cred.setAccessToken(accessToken);
|
||||
} else {
|
||||
handleNeedAuthorization(projectId, username, request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
servletRequest.setAttribute(ATTRIBUTE_KEY, cred);
|
||||
filterChain.doFilter(
|
||||
servletRequest,
|
||||
servletResponse
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
private void handleNeedAuthorization(
|
||||
String projectName,
|
||||
String userName,
|
||||
int statusCode,
|
||||
HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Unauthorized, User '{}' status={} ip={}",
|
||||
projectName,
|
||||
userName,
|
||||
statusCode,
|
||||
servletRequest.getRemoteAddr()
|
||||
);
|
||||
HttpServletResponse response = servletResponse;
|
||||
response.setContentType("text/plain");
|
||||
response.setHeader("WWW-Authenticate", "Basic realm=\"Git Bridge\"");
|
||||
PrintWriter w = response.getWriter();
|
||||
if (statusCode == 429) {
|
||||
// Rate limit
|
||||
response.setStatus(429);
|
||||
w.println(
|
||||
"Rate limit exceeded. Please wait and try again later."
|
||||
);
|
||||
} else {
|
||||
response.setStatus(401);
|
||||
w.println(
|
||||
"Please sign in using your email address and Overleaf password."
|
||||
);
|
||||
w.println();
|
||||
w.println(
|
||||
"*Note*: if you sign in to Overleaf using another provider, "
|
||||
+ "such "
|
||||
);
|
||||
w.println(
|
||||
"as Google or Twitter, you need to set a password "
|
||||
+ "on your Overleaf "
|
||||
);
|
||||
w.println(
|
||||
"account first. "
|
||||
+ "Please see https://www.overleaf.com/learn/how-to/Troubleshooting_git_bridge_problems "
|
||||
);
|
||||
w.println("for more information.");
|
||||
}
|
||||
|
||||
w.close();
|
||||
private boolean isLinkSharingId(String projectId) {
|
||||
return projectId.matches("^[0-9]+[bcdfghjklmnpqrstvwxyz]{6,12}$");
|
||||
}
|
||||
|
||||
private void handleMissingRepository(
|
||||
String projectName,
|
||||
MissingRepositoryException e,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info("[{}] Project missing.", projectName);
|
||||
private boolean isProjectId(String projectId) {
|
||||
return projectId.matches("^[0-9a-f]{24}$");
|
||||
}
|
||||
|
||||
private void sendResponse(HttpServletResponse response, int code, List<String> lines) throws IOException {
|
||||
response.setContentType("text/plain");
|
||||
|
||||
// git special-cases 404 to give "repository '%s' not found",
|
||||
// rather than displaying the raw status code.
|
||||
response.setStatus(404);
|
||||
|
||||
response.setStatus(code);
|
||||
PrintWriter w = response.getWriter();
|
||||
for (String line : e.getDescriptionLines()) {
|
||||
for (String line : lines) {
|
||||
w.println(line);
|
||||
}
|
||||
w.close();
|
||||
}
|
||||
|
||||
private void handleLinkSharingId(
|
||||
String projectId,
|
||||
String username,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Bad project id, User '{}' ip={}",
|
||||
projectId,
|
||||
username,
|
||||
getClientIp(request)
|
||||
);
|
||||
sendResponse(response, 404, Arrays.asList(
|
||||
"Git access via link sharing link is not supported.",
|
||||
"",
|
||||
"You can find the project's git remote url by opening it in your browser",
|
||||
"and selecting Git from the left sidebar in the project view.",
|
||||
"",
|
||||
"If this is unexpected, please contact us at support@overleaf.com, or",
|
||||
"see https://www.overleaf.com/help/342 for more information."
|
||||
));
|
||||
}
|
||||
|
||||
private void handleBadProjectId(
|
||||
String projectId,
|
||||
String username,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Bad project id, User '{}' ip={}",
|
||||
projectId,
|
||||
username,
|
||||
getClientIp(request)
|
||||
);
|
||||
sendResponse(response, 404, Arrays.asList(
|
||||
"This Overleaf project does not exist.",
|
||||
"",
|
||||
"If this is unexpected, please contact us at support@overleaf.com, or",
|
||||
"see https://www.overleaf.com/help/342 for more information."
|
||||
));
|
||||
}
|
||||
|
||||
private void handleRateLimit(
|
||||
String projectId,
|
||||
String username,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Rate limit, User '{}' ip={}",
|
||||
projectId,
|
||||
username,
|
||||
getClientIp(request)
|
||||
);
|
||||
sendResponse(response, 429, Arrays.asList(
|
||||
"Rate limit exceeded. Please wait and try again later."
|
||||
));
|
||||
}
|
||||
|
||||
private void handleNeedAuthorization(
|
||||
String projectId,
|
||||
String username,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Unauthorized, User '{}' ip={}",
|
||||
projectId,
|
||||
username,
|
||||
getClientIp(request)
|
||||
);
|
||||
response.setHeader("WWW-Authenticate", "Basic realm=\"Git Bridge\"");
|
||||
if (this.isUserPasswordEnabled) {
|
||||
sendResponse(response, 401, Arrays.asList(
|
||||
"Log in using the email address and password you use for Overleaf.",
|
||||
"",
|
||||
"*Note*: if you use a provider such as Google or Twitter to sign into",
|
||||
"your Overleaf account, you will need to set a password.",
|
||||
"",
|
||||
"See our help page for more support:",
|
||||
"https://www.overleaf.com/learn/how-to/Troubleshooting_git_bridge_problems"
|
||||
));
|
||||
} else {
|
||||
sendResponse(response, 401, Arrays.asList(
|
||||
"Log in with the username 'git' and enter your Git authentication token",
|
||||
"when prompted for a password.",
|
||||
"",
|
||||
"You can generate and manage your Git authentication tokens in",
|
||||
"your Overleaf Account Settings."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBadAccessToken(
|
||||
String projectId,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] Bad access token, ip={}",
|
||||
projectId,
|
||||
getClientIp(request)
|
||||
);
|
||||
sendResponse(response, 401, Arrays.asList(
|
||||
"Enter your Git authentication token when prompted for a password.",
|
||||
"",
|
||||
"You can generate and manage your Git authentication tokens in",
|
||||
"your Overleaf Account Settings."
|
||||
));
|
||||
}
|
||||
|
||||
private int checkAccessToken(
|
||||
Oauth2 oauth2,
|
||||
String accessToken,
|
||||
String clientIp
|
||||
) throws IOException {
|
||||
GenericUrl url = new GenericUrl(
|
||||
oauth2.getOauth2Server() + "/oauth/token/info?client_ip=" + clientIp
|
||||
);
|
||||
HttpRequest request = Instance.httpRequestFactory.buildGetRequest(url);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAuthorization("Bearer " + accessToken);
|
||||
request.setHeaders(headers);
|
||||
request.setThrowExceptionOnExecuteError(false);
|
||||
HttpResponse response = request.execute();
|
||||
int statusCode = response.getStatusCode();
|
||||
response.disconnect();
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
private void handleUnknownOauthServerError(
|
||||
String projectId,
|
||||
int statusCode,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
Log.info(
|
||||
"[{}] OAuth server error, statusCode={}, ip={}",
|
||||
projectId,
|
||||
statusCode,
|
||||
getClientIp(request)
|
||||
);
|
||||
sendResponse(response, 500, Arrays.asList(
|
||||
"Unexpected server error. Please try again later."
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remote IP from the request.
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String clientIp = request.getHeader("X-Forwarded-For");
|
||||
if (clientIp == null) {
|
||||
clientIp = request.getRemoteAddr();
|
||||
}
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract basic auth credentials from the request.
|
||||
*
|
||||
* Returns null if valid basic auth credentials couldn't be found.
|
||||
*/
|
||||
private BasicAuthCredentials getBasicAuthCredentials(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringTokenizer st = new StringTokenizer(authHeader);
|
||||
if (!st.hasMoreTokens()) {
|
||||
return null;
|
||||
}
|
||||
String basic = st.nextToken();
|
||||
if (!basic.equalsIgnoreCase("Basic")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String credentials = null;
|
||||
try {
|
||||
credentials = new String(
|
||||
Base64.decodeBase64(st.nextToken()),
|
||||
"UTF-8"
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] split = credentials.split(":", 2);
|
||||
if (split.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String username = split[0];
|
||||
String password = split[1];
|
||||
return new BasicAuthCredentials(username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a password grant flow with the OAuth server and return an access token.
|
||||
*
|
||||
* The access token is null if the password grant flow was unsuccessful.
|
||||
*/
|
||||
private String doPasswordGrantFlow(
|
||||
String username,
|
||||
String password,
|
||||
String clientIp
|
||||
) throws IOException {
|
||||
return new PasswordTokenRequest(
|
||||
Instance.httpTransport,
|
||||
Instance.jsonFactory,
|
||||
new GenericUrl(
|
||||
oauth2.getOauth2Server()
|
||||
+ "/oauth/token?client_ip="
|
||||
+ clientIp
|
||||
),
|
||||
username,
|
||||
password
|
||||
).setClientAuthentication(
|
||||
new ClientParametersAuthentication(
|
||||
oauth2.getOauth2ClientID(),
|
||||
oauth2.getOauth2ClientSecret()
|
||||
)
|
||||
).execute().getAccessToken();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package uk.ac.ic.wlgitbridge.snapshot.servermock.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
public class MockOAuthRequestHandler extends AbstractHandler {
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
String method = baseRequest.getMethod();
|
||||
if (method.equals("GET") && target.equals("/oauth/token/info")) {
|
||||
response.setContentType("application/json");
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.getWriter().println("{}");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.server;
|
|||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||
import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponseBuilder;
|
||||
import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState;
|
||||
|
@ -22,7 +23,11 @@ public class MockSnapshotServer {
|
|||
public MockSnapshotServer(int port, File resourceBase) {
|
||||
server = new Server(port);
|
||||
responseBuilder = new SnapshotResponseBuilder();
|
||||
server.setHandler(getHandlerForResourceBase(resourceBase));
|
||||
|
||||
HandlerList handlers = new HandlerList();
|
||||
handlers.addHandler(new MockOAuthRequestHandler());
|
||||
handlers.addHandler(getHandlerForResourceBase(resourceBase));
|
||||
server.setHandler(handlers);
|
||||
}
|
||||
|
||||
private HandlerCollection getHandlerForResourceBase(File resourceBase) {
|
||||
|
|
|
@ -51,6 +51,10 @@ public class WLGitBridgeIntegrationTest {
|
|||
|
||||
private Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
private static final String PROJECT_ID = "000000000000000000000000";
|
||||
private static final String PROJECT_ID1 = "111111111111111111111111";
|
||||
private static final String PROJECT_ID2 = "222222222222222222222222";
|
||||
|
||||
private Map<String, Map<String, SnapshotAPIState>> states = new HashMap<String, Map<String, SnapshotAPIState>>() {{
|
||||
put("canCloneARepository", new HashMap<String, SnapshotAPIState>() {{
|
||||
put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneARepository/state/state.json")).build());
|
||||
|
@ -185,7 +189,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
}
|
||||
|
||||
private File gitClone(String repositoryName, int port, File dir) throws IOException, InterruptedException {
|
||||
String repo = "git clone http://127.0.0.1:" + port + "/" + repositoryName + ".git";
|
||||
String repo = "git clone http://git:password@127.0.0.1:" + port + "/" + repositoryName;
|
||||
Process gitProcess = runtime.exec(repo, null, dir);
|
||||
int exitCode = gitProcess.waitFor();
|
||||
if (exitCode != 0) {
|
||||
|
@ -259,8 +263,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33857, 3857)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33857, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneARepository/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33857, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneARepository/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -272,10 +276,10 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33858, 3858)
|
||||
});
|
||||
wlgb.run();
|
||||
File testproj1Dir = gitClone("testproj1", 33858, dir);
|
||||
File testproj2Dir = gitClone("testproj2", 33858, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj1"), testproj1Dir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj2"), testproj2Dir.toPath()));
|
||||
File testproj1Dir = gitClone(PROJECT_ID1, 33858, dir);
|
||||
File testproj2Dir = gitClone(PROJECT_ID2, 33858, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID1), testproj1Dir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID2), testproj2Dir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -287,11 +291,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33859, 3859)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33859, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33859, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedTexFile").get("withModifiedTexFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/withModifiedTexFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/withModifiedTexFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -303,11 +307,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33860, 3860)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33860, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33860, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADeletedTexFile").get("withDeletedTexFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -319,11 +323,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33862, 3862)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33862, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33862, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedBinaryFile").get("withModifiedBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -335,11 +339,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33863, 3863)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33863, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33863, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADeletedBinaryFile").get("withDeletedBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -351,11 +355,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(44001, 4001)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 44001, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 44001, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADuplicateBinaryFile").get("withDuplicateBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -367,8 +371,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(44002, 4002)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 44002, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneDuplicateBinaryFiles/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 44002, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneDuplicateBinaryFiles/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -380,11 +384,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(44003, 4003)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 44003, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 44003, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullUpdatedBinaryFiles").get("withUpdatedBinaryFiles"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -396,11 +400,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33864, 3864)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33864, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33864, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedNestedFile").get("withModifiedNestedFile"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -412,11 +416,11 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33865, 3865)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33865, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/base/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33865, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/base/" + PROJECT_ID), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullDeletedNestedFiles").get("withDeletedNestedFiles"));
|
||||
gitPull(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -428,8 +432,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
wlgb.run();
|
||||
server.setState(states.get("canPushFilesSuccessfully").get("state"));
|
||||
File testprojDir = gitClone("testproj", 33866, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPushFilesSuccessfully/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33866, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPushFilesSuccessfully/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
assertEquals(0, runtime.exec("touch push.tex", null, testprojDir).waitFor());
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -437,7 +441,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
}
|
||||
|
||||
private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST =
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33867/testproj.git'\n" +
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33867/" + PROJECT_ID + "'\n" +
|
||||
"hint: Updates were rejected because the tip of your current branch is behind\n" +
|
||||
"hint: its remote counterpart. Integrate the remote changes (e.g.\n" +
|
||||
"hint: 'git pull ...') before pushing again.\n" +
|
||||
|
@ -452,8 +456,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33867, 3867)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33867, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnFirstStageOutOfDate/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33867, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnFirstStageOutOfDate/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -462,7 +466,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
}
|
||||
|
||||
private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND =
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33868/testproj.git'\n" +
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33868/" + PROJECT_ID + "'\n" +
|
||||
"hint: Updates were rejected because the tip of your current branch is behind\n" +
|
||||
"hint: its remote counterpart. Integrate the remote changes (e.g.\n" +
|
||||
"hint: 'git pull ...') before pushing again.\n" +
|
||||
|
@ -477,8 +481,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33868, 3868)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33868, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnSecondStageOutOfDate/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33868, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnSecondStageOutOfDate/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -492,9 +496,9 @@ public class WLGitBridgeIntegrationTest {
|
|||
"remote: hint: file2.exe (invalid file extension)",
|
||||
"remote: hint: hello world.png (rename to: hello_world.png)",
|
||||
"remote: hint: an image.jpg (rename to: an_image.jpg)",
|
||||
"To http://127.0.0.1:33869/testproj.git",
|
||||
"To http://127.0.0.1:33869/" + PROJECT_ID,
|
||||
"! [remote rejected] master -> master (invalid files)",
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33869/testproj.git'"
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33869/" + PROJECT_ID + "'"
|
||||
);
|
||||
|
||||
@Test
|
||||
|
@ -506,8 +510,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33869, 3869)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33869, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidFiles/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33869, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidFiles/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -519,9 +523,9 @@ public class WLGitBridgeIntegrationTest {
|
|||
private static final List<String> EXPECTED_OUT_PUSH_INVALID_PROJECT = Arrays.asList(
|
||||
"remote: hint: project: no main file",
|
||||
"remote: hint: The project would have no (editable) main .tex file.",
|
||||
"To http://127.0.0.1:33870/testproj.git",
|
||||
"To http://127.0.0.1:33870/" + PROJECT_ID,
|
||||
"! [remote rejected] master -> master (invalid project)",
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33870/testproj.git'"
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33870/" + PROJECT_ID + "'"
|
||||
);
|
||||
|
||||
@Test
|
||||
|
@ -533,8 +537,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33870, 3870)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33870, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidProject/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33870, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidProject/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -546,9 +550,9 @@ public class WLGitBridgeIntegrationTest {
|
|||
private static final List<String> EXPECTED_OUT_PUSH_UNEXPECTED_ERROR = Arrays.asList(
|
||||
"remote: hint: There was an internal error with the Overleaf server.",
|
||||
"remote: hint: Please contact Overleaf.",
|
||||
"To http://127.0.0.1:33871/testproj.git",
|
||||
"To http://127.0.0.1:33871/" + PROJECT_ID,
|
||||
"! [remote rejected] master -> master (Overleaf error)",
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33871/testproj.git'"
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33871/" + PROJECT_ID + "'"
|
||||
);
|
||||
|
||||
/* this one prints a stack trace */
|
||||
|
@ -561,8 +565,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33871, 3871)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33871, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnUnexpectedError/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, 33871, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnUnexpectedError/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -576,9 +580,9 @@ public class WLGitBridgeIntegrationTest {
|
|||
"remote:",
|
||||
"remote: hint: You have 1 invalid files in your Overleaf project:",
|
||||
"remote: hint: file1.exe (invalid file extension)",
|
||||
"To http://127.0.0.1:33872/testproj.git",
|
||||
"To http://127.0.0.1:33872/" + PROJECT_ID,
|
||||
"! [remote rejected] master -> master (invalid files)",
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33872/testproj.git'"
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33872/" + PROJECT_ID + "'"
|
||||
);
|
||||
|
||||
@Test
|
||||
|
@ -590,10 +594,10 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33872, 3872)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33872, dir);
|
||||
File testprojDir = gitClone(PROJECT_ID, 33872, dir);
|
||||
|
||||
// try to push invalid file; it should fail
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/invalidState/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/invalidState/" + PROJECT_ID), testprojDir.toPath()));
|
||||
assertEquals(0, runtime.exec("touch file1.exe", null, testprojDir).waitFor());
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -606,7 +610,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
gitCommit(testprojDir, "remove_invalid_file");
|
||||
server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("validState"));
|
||||
gitPush(testprojDir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/testproj"), testprojDir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -629,8 +633,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
wlgb.run();
|
||||
|
||||
File testprojDir = gitClone("testproj", gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canServePushedFiles/state/testproj"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canServePushedFiles/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
runtime.exec("touch push.tex", null, testprojDir).waitFor();
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
|
@ -677,17 +681,17 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
wlgb.run();
|
||||
File rootGitDir = new File(wlgb.config.getRootGitDirectory());
|
||||
File testProj1ServerDir = new File(rootGitDir, "testproj1");
|
||||
File testProj2ServerDir = new File(rootGitDir, "testproj2");
|
||||
File testProj1Dir = gitClone("testproj1", 33874, dir);
|
||||
File testProj1ServerDir = new File(rootGitDir, PROJECT_ID1);
|
||||
File testProj2ServerDir = new File(rootGitDir, PROJECT_ID2);
|
||||
File testProj1Dir = gitClone(PROJECT_ID1, 33874, dir);
|
||||
assertTrue(testProj1ServerDir.exists());
|
||||
assertFalse(testProj2ServerDir.exists());
|
||||
gitClone("testproj2", 33874, dir);
|
||||
gitClone(PROJECT_ID2, 33874, dir);
|
||||
while (testProj1ServerDir.exists());
|
||||
assertFalse(testProj1ServerDir.exists());
|
||||
assertTrue(testProj2ServerDir.exists());
|
||||
FileUtils.deleteDirectory(testProj1Dir);
|
||||
gitClone("testproj1", 33874, dir);
|
||||
gitClone(PROJECT_ID1, 33874, dir);
|
||||
while (testProj2ServerDir.exists());
|
||||
assertTrue(testProj1ServerDir.exists());
|
||||
assertFalse(testProj2ServerDir.exists());
|
||||
|
@ -697,9 +701,9 @@ public class WLGitBridgeIntegrationTest {
|
|||
"remote: hint: Your Git repository contains a reference we cannot resolve.",
|
||||
"remote: hint: If your project contains a Git submodule,",
|
||||
"remote: hint: please remove it and try again.",
|
||||
"To http://127.0.0.1:33875/testproj.git",
|
||||
"To http://127.0.0.1:33875/" + PROJECT_ID,
|
||||
"! [remote rejected] master -> master (invalid git repo)",
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33875/testproj.git'"
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33875/" + PROJECT_ID + "'"
|
||||
);
|
||||
|
||||
@Test
|
||||
|
@ -711,7 +715,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(33875, 3875)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33875, dir);
|
||||
File testprojDir = gitClone(PROJECT_ID, 33875, dir);
|
||||
runtime.exec("mkdir sub", null, testprojDir).waitFor();
|
||||
File sub = new File(testprojDir, "sub");
|
||||
runtime.exec("touch sub.txt", null, sub).waitFor();
|
||||
|
@ -749,12 +753,12 @@ public class WLGitBridgeIntegrationTest {
|
|||
assertEquals(404, response.getStatusCode());
|
||||
assertEquals("{\"message\":\"HTTP error 404\"}", response.getResponseBody());
|
||||
|
||||
// With an unsupported URL outside the api, we should get a 500,
|
||||
// which is rendered by our custom error handler.
|
||||
// With an unsupported URL outside the api, the request is assumed to
|
||||
// be from a git client and we should get a 401 because the request
|
||||
// does not include basic auth credentials.
|
||||
url = "http://127.0.0.1:" + gitBridgePort + "/foo";
|
||||
response = asyncHttpClient().prepareGet(url).execute().get();
|
||||
assertEquals(500, response.getStatusCode());
|
||||
assertEquals("{\"message\":\"HTTP error 500\"}", response.getResponseBody());
|
||||
assertEquals(401, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -770,7 +774,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
|
@ -787,7 +791,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
|
@ -804,7 +808,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
|
@ -819,10 +823,10 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", gitBridgePort, dir);
|
||||
File testprojDir2 = gitClone("testproj2", gitBridgePort, dir);
|
||||
File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir);
|
||||
File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir);
|
||||
// Second project content is equal to content of the first
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canMigrateRepository/state/testproj"), testprojDir2.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canMigrateRepository/state/" + PROJECT_ID), testprojDir2.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -837,8 +841,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
wlgb.run();
|
||||
// don't clone the source project first
|
||||
File testprojDir2 = gitClone("testproj2", gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/skipMigrationWhenMigratedFromMissing/state/testproj2"), testprojDir2.toPath()));
|
||||
File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/skipMigrationWhenMigratedFromMissing/state/" + PROJECT_ID2), testprojDir2.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -852,8 +856,8 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj_no_change", gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneAMigratedRepositoryWithoutChanges/state/testproj_no_change"), testprojDir.toPath()));
|
||||
File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir);
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneAMigratedRepositoryWithoutChanges/state/" + PROJECT_ID), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -867,7 +871,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/1234bbccddff.git", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/1234bbccddff.git", null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
|
@ -884,7 +888,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/conflict.git", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/conflict.git", null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
wlgb.stop();
|
||||
}
|
||||
|
@ -902,7 +906,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/project/1234abcd", null, dir);
|
||||
Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/project/1234abcd", null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
|
||||
List<String> actual = Util.linesFromStream(gitProcess.getErrorStream(), 0, "");
|
||||
|
@ -996,7 +1000,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
});
|
||||
wlgb.run();
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
String urlBase = "http://127.0.0.1:" + gitBridgePort;
|
||||
String urlBase = "http://git:password@127.0.0.1:" + gitBridgePort;
|
||||
HttpPost gitLfsRequest = new HttpPost(urlBase+"/5f2419407929eb0026641967.git/info/lfs/objects/batch");
|
||||
HttpResponse gitLfsResponse = client.execute(gitLfsRequest);
|
||||
assertEquals(422, gitLfsResponse.getStatusLine().getStatusCode());
|
||||
|
@ -1016,7 +1020,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testProjDir = gitClone("testproj", gitBridgePort, dir);
|
||||
File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir);
|
||||
File one = new File(testProjDir, "sub/one.txt");
|
||||
one.createNewFile();
|
||||
FileWriter fw = new FileWriter(one.getPath());
|
||||
|
@ -1044,7 +1048,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testProjDir = gitClone("testproj", gitBridgePort, dir);
|
||||
File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir);
|
||||
server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile"));
|
||||
gitPull(testProjDir);
|
||||
File f = new File(testProjDir.getPath() + "/sub/one.txt");
|
||||
|
@ -1084,7 +1088,7 @@ public class WLGitBridgeIntegrationTest {
|
|||
" \"oauth2\": {\n" +
|
||||
" \"oauth2ClientID\": \"clientID\",\n" +
|
||||
" \"oauth2ClientSecret\": \"oauth2 client secret\",\n" +
|
||||
" \"oauth2Server\": \"https://www.overleaf.com\"\n" +
|
||||
" \"oauth2Server\": \"http://127.0.0.1:" + apiPort + "\"\n" +
|
||||
" }";
|
||||
if (swapCfg != null) {
|
||||
cfgStr += ",\n" +
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj_no_change",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 0,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3857/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3857/state/000000000000000000000000/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,11 +31,11 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:4002/state/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4002/state/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410-copy.png"
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4002/state/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4002/state/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj1",
|
||||
"project": "111111111111111111111111",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3858/state/testproj1/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3858/state/111111111111111111111111/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"project": "testproj2",
|
||||
"project": "222222222222222222222222",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -75,7 +75,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3858/state/testproj2/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png",
|
||||
"url": "http://127.0.0.1:3858/state/222222222222222222222222/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png",
|
||||
"path": "editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png"
|
||||
}
|
||||
]
|
||||
|
@ -87,4 +87,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
@ -1,12 +1,12 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj2",
|
||||
"project": "222222222222222222222222",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
"email": "jdleesmiller+1@gmail.com",
|
||||
"name": "John+1",
|
||||
"migratedFromId": "testproj"
|
||||
"migratedFromId": "000000000000000000000000"
|
||||
},
|
||||
"getSavedVers": [],
|
||||
"getForVers": [
|
||||
|
@ -24,7 +24,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3881/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3881/state/000000000000000000000000/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
@ -37,7 +37,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -68,7 +68,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3881/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3881/state/000000000000000000000000/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3863/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3863/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -53,7 +53,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3863/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3863/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -65,4 +65,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3860/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3860/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -34,7 +34,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3860/withDeletedTexFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3860/withDeletedTexFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -53,7 +53,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3860/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3860/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -65,4 +65,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:4001/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4001/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -38,11 +38,11 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410-copy.png"
|
||||
}
|
||||
]
|
||||
|
@ -61,7 +61,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4001/withDuplicateBinaryFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -73,4 +73,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3862/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3862/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3862/withModifiedBinaryFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3862/withModifiedBinaryFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -57,7 +57,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3862/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3862/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -69,4 +69,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3864/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3864/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -46,7 +46,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3864/withModifiedNestedFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3864/withModifiedNestedFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -65,7 +65,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3864/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3864/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -77,4 +77,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3859/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3859/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -43,4 +43,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3859/withModifiedTexFile/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3859/withModifiedTexFile/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -57,7 +57,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3859/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3859/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -69,4 +69,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -39,7 +39,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3865/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3865/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -51,4 +51,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 2,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3865/withDeletedNestedFiles/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3865/withDeletedNestedFiles/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -65,7 +65,7 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3865/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:3865/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
}
|
||||
]
|
||||
|
@ -77,4 +77,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 5,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"project": "000000000000000000000000",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
|
@ -31,11 +31,11 @@
|
|||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:4003/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4003/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410.png"
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4003/base/testproj/overleaf-white-410.png",
|
||||
"url": "http://127.0.0.1:4003/base/000000000000000000000000/overleaf-white-410.png",
|
||||
"path": "overleaf-white-410-copy.png"
|
||||
}
|
||||
]
|
||||
|
@ -47,4 +47,4 @@
|
|||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |