package org.koshinuke.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.eclipse.jgit.util.StringUtils;
import org.koshinuke.conf.Configuration;
import org.koshinuke.jersey.Csrf;
import org.koshinuke.jersey.auth.Auth;
import org.koshinuke.logic.GitDelegate;
import org.koshinuke.model.BlameModel;
import org.koshinuke.model.BlobModel;
import org.koshinuke.model.BranchHistoryModel;
import org.koshinuke.model.CommitModel;
import org.koshinuke.model.DiffModel;
import org.koshinuke.model.KoshinukePrincipal;
import org.koshinuke.model.NodeModel;
import org.koshinuke.model.RepositoryModel;
import org.koshinuke.util.ServletUtil;
import com.sun.jersey.api.representation.Form;
import com.sun.jersey.spi.resource.Singleton;
/**
* @author taichi
*/
@Auth
@Singleton
@Path("/api/1.0")
@Produces(MediaType.APPLICATION_JSON)
public class RepositoryService {
final Logger LOG = Logger.getLogger(RepositoryService.class.getName());
Map<String, FormAction> command = new HashMap<>();
{
this.command.put("init", new FormAction() {
@Override
public Response execute(KoshinukePrincipal p, Form form) {
return RepositoryService.this.initRepository(p, form);
}
});
this.command.put("clone", new FormAction() {
@Override
public Response execute(KoshinukePrincipal p, Form form) {
return RepositoryService.this.cloneRepository(p, form);
}
});
}
GitDelegate git;
public RepositoryService(@Context Configuration config) {
this.git = new GitDelegate(config);
}
@GET
public List<RepositoryModel> listRepository() {
return this.git.listRepository();
}
@Csrf
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response executeCommand(@Context KoshinukePrincipal p, Form form) {
FormAction action = this.command.get(form.getFirst("!"));
if (action != null) {
return action.execute(p, form);
}
return Response.status(Status.FORBIDDEN).build();
}
protected Response initRepository(KoshinukePrincipal p, Form form) {
String name = form.getFirst("rn");
String readme = form.getFirst("rr");
if (StringUtils.isEmptyOrNull(name) == false
&& StringUtils.isEmptyOrNull(readme) == false) {
String[] ary = name.split("/");
if (ary.length == 2 && this.git.initRepository(p, name, readme)) {
return Response.status(HttpServletResponse.SC_CREATED)
.entity(this.listRepository()).build();
}
}
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
protected Response cloneRepository(KoshinukePrincipal p, Form form) {
String uri = form.getFirst("uri");
String un = form.getFirst("un");
String up = form.getFirst("up");
if (StringUtils.isEmptyOrNull(uri) == false) {
if (this.git.cloneRepository(p, uri, un, up)) {
return Response.status(HttpServletResponse.SC_CREATED)
.entity(this.listRepository()).build();
}
}
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
protected static final Pattern isNumeric = Pattern.compile("[0-9]+");
protected static int to(String s, int dv) {
if (s != null && isNumeric.matcher(s).matches()) {
return Integer.parseInt(s);
}
return dv;
}
static final String REV_PART = "{rev: ([\\w/\\-\\+\\.]|%[0-9a-fA-F]{2})+}";
@GET
@Path("/{project}/{repository}/tree/" + REV_PART)
public Response tree(@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("rev") String rev, @QueryParam("offset") String offset,
// TODO config ?
@QueryParam("limit") String limit) {
List<NodeModel> list = this.git.listRepository(project, repository,
rev, to(offset, 0), to(limit, 512));
if (list == null) {
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
} else {
return Response.ok(list).build();
}
}
@GET
@Path("/{project}/{repository}/blob/" + REV_PART)
public Response blob(@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("rev") String rev) {
BlobModel blob = this.git.getBlob(project, repository, rev);
if (blob == null) {
return Response.noContent().build();
}
return Response.ok(blob).build();
}
@Csrf
@POST
@Path("/{project}/{repository}/blob/" + REV_PART)
@Consumes(MediaType.APPLICATION_JSON)
public Response commit(@Context KoshinukePrincipal p,
@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("rev") String rev, BlobModel input) {
if (StringUtils.isEmptyOrNull(input.getContent()) == false
&& StringUtils.isEmptyOrNull(input.getMessage()) == false) {
BlobModel blob = this.git.modifyBlob(p, project, repository, rev,
input);
if (blob != null) {
return Response.ok(blob).build();
}
}
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
@GET
@Path("/{project}/{repository}/history")
public Response histories(@PathParam("project") String project,
@PathParam("repository") String repository) {
List<BranchHistoryModel> list = this.git.getHistories(project,
repository);
if (list == null) {
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
return Response.ok(list).build();
}
@GET
@Path("/{project}/{repository}/commits/" + REV_PART)
public Response commits(@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("rev") String rev, @QueryParam("offset") String offset,
// TODO config ?
@QueryParam("limit") String limit) {
List<CommitModel> list = this.git.getCommits(project, repository, rev,
offset, to(limit, 128));
if (list == null) {
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
return Response.ok(list).build();
}
@GET
@Path("/{project}/{repository}/commit/{commitid: [a-zA-Z0-9]{2,40}}")
public Response diff(@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("commitid") String commitid) {
DiffModel model = this.git.getDiff(project, repository, commitid);
if (model == null) {
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
return Response.ok(model).build();
}
@GET
@Path("/{project}/{repository}/blame/" + REV_PART)
public Response blame(@PathParam("project") String project,
@PathParam("repository") String repository,
@PathParam("rev") String rev) {
List<BlameModel> list = this.git.getBlame(project, repository, rev);
if (list == null) {
return Response.status(ServletUtil.SC_UNPROCESSABLE_ENTITY).build();
}
return Response.ok(list).build();
}
}