ProjectSetRequestHandler as REST API
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Tue, 3 Feb 2015 09:09:41 +0000 (10:09 +0100)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Tue, 3 Feb 2015 09:12:08 +0000 (10:12 +0100)
build/restApiDocGenerator/gulpfile.js
platform/platform-impl/src/com/intellij/platform/ProjectSetReader.java
platform/platform-impl/src/com/intellij/platform/ProjectSetRequestHandler.coffee [new file with mode: 0644]
platform/platform-impl/src/com/intellij/platform/ProjectSetRequestHandler.java
platform/platform-impl/src/org/jetbrains/ide/RestService.java
platform/platform-tests/platform-tests.iml
platform/platform-tests/testSrc/com/intellij/platform/ProjectSetTest.java

index e583e04c7c23cf2d3201b664391806d6806476c5..49e284850ec1fcc0afe9897e71048f6c3e514bcd 100644 (file)
@@ -2,7 +2,7 @@ var gulp = require('gulp')
 var apidoc = require('gulp-apidoc')
 var path = require('path')
 
-var sources = path.normalize("../../platform/platform-impl/src/org/jetbrains/ide")
+var sources = path.normalize("../../platform/platform-impl/src")
 
 gulp.task('apidoc', function () {
   apidoc.exec({src: sources, dest: (process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE) + "/idea-rest-api"})
index 25139a1db98bdc93698f7a8452c8cc8de8461c73..fbee288da7678dda82d878de46d9d3d0c6943a8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 package com.intellij.platform;
 
-import com.google.gson.*;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.Pair;
 import com.intellij.projectImport.ProjectSetProcessor;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
-import org.intellij.lang.annotations.Language;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -31,29 +32,17 @@ import java.util.*;
  * @author Dmitry Avdeev
  */
 public class ProjectSetReader {
-
-  public void readDescriptor(@Language("JSON") @NotNull String descriptor, @Nullable ProjectSetProcessor.Context context) {
-
-    ProjectSetProcessor[] extensions = ProjectSetProcessor.EXTENSION_POINT_NAME.getExtensions();
+  public void readDescriptor(@NotNull JsonObject descriptor, @Nullable ProjectSetProcessor.Context context) {
     Map<String, ProjectSetProcessor> processors = new HashMap<String, ProjectSetProcessor>();
-    for (ProjectSetProcessor extension : extensions) {
+    for (ProjectSetProcessor extension : ProjectSetProcessor.EXTENSION_POINT_NAME.getExtensions()) {
       processors.put(extension.getId(), extension);
     }
 
-    JsonElement parse;
-    try {
-      parse = new JsonParser().parse(descriptor);
-    }
-    catch (JsonSyntaxException e) {
-      LOG.error(e);
-      return;
-    }
-    Iterator<Map.Entry<String, JsonElement>> iterator = parse.getAsJsonObject().entrySet().iterator();
     if (context == null) {
       context = new ProjectSetProcessor.Context();
     }
     context.directoryName = "";
-    runProcessor(processors, context, iterator);
+    runProcessor(processors, context, descriptor.entrySet().iterator());
   }
 
   private static void runProcessor(final Map<String, ProjectSetProcessor> processors, final ProjectSetProcessor.Context context, final Iterator<Map.Entry<String, JsonElement>> iterator) {
diff --git a/platform/platform-impl/src/com/intellij/platform/ProjectSetRequestHandler.coffee b/platform/platform-impl/src/com/intellij/platform/ProjectSetRequestHandler.coffee
new file mode 100644 (file)
index 0000000..ce1f91f
--- /dev/null
@@ -0,0 +1,35 @@
+###
+  @apiDefine OpenProjectSetRequestExample
+
+  @apiExample {json} Request-Example:
+{
+  "vcs": {
+    "git": {"url": "https://github.com/JetBrains/idea-templates.git"}
+  },
+  "project": "/spring/SpringApp"
+}
+###
+
+###
+  @apiDefine OpenProjectSetRequestExampleMulti
+
+  @apiExample {json} Request-Example (multi-repository):
+{
+  "vcs": {
+    "git": [
+      {
+        "url": "git@git.labs.intellij.net:idea/community"
+      },
+      {
+        "url": "git://git.jetbrains.org/idea/android.git",
+        "targetDir": "android"
+      },
+      {
+        "url": "git://git.jetbrains.org/idea/adt-tools-base.git",
+        "targetDir": "android/tools-base"
+      }
+    ]
+  },
+  "project": ""
+}
+###
\ No newline at end of file
index 865de871a26d5ec8a36b8d6415982520204b20fa..8a9ad24adfd4ee64bcdfd7c92efd4dcfa42517c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 package com.intellij.platform;
 
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
 import com.intellij.openapi.application.ApplicationManager;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.http.FullHttpRequest;
 import io.netty.handler.codec.http.HttpMethod;
-import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.QueryStringDecoder;
-import org.intellij.lang.annotations.Language;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.ide.HttpRequestHandler;
-import org.jetbrains.io.Responses;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.ide.RestService;
 
 import java.io.IOException;
-import java.nio.charset.Charset;
 
 /**
  * @author Dmitry Avdeev
+ *
+ * @api {post} /openProjectSet Open project
+ * @apiName openProjectSet
+ * @apiGroup Platform
+ * @apiDescription Checkout a repository from source control and then open an IDEA project from it.
+ *
+ * @apiParam (properties) {Object} vcs The map of the VCS.
+ * @apiParam (properties) {String} project The path to project to be opened.
+ *
+ * @apiUse OpenProjectSetRequestExample
+ * @apiUse OpenProjectSetRequestExampleMulti
  */
-public class ProjectSetRequestHandler extends HttpRequestHandler {
+public class ProjectSetRequestHandler extends RestService {
+  @Override
+  protected boolean isMethodSupported(@NotNull HttpMethod method) {
+    return method == HttpMethod.POST;
+  }
 
+  @NotNull
   @Override
-  public boolean isSupported(@NotNull FullHttpRequest request) {
-    return request.method() == HttpMethod.POST && "/openProjectSet".equals(request.uri());
+  protected String getServiceName() {
+    return "openProjectSet";
   }
 
   @Override
-  public boolean process(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context)
-    throws IOException {
+  protected boolean isPrefixlessAllowed() {
+    return true;
+  }
 
-    @Language("JSON") final String desc = request.content().toString(Charset.defaultCharset());
+  @Nullable
+  @Override
+  public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException {
+    final JsonObject descriptor = new JsonParser().parse(createJsonReader(request)).getAsJsonObject();
     ApplicationManager.getApplication().invokeLater(new Runnable() {
       @Override
       public void run() {
-        new ProjectSetReader().readDescriptor(desc, null);
+        new ProjectSetReader().readDescriptor(descriptor, null);
       }
     });
-    Responses.sendStatus(HttpResponseStatus.OK, context.channel(), request);
-    return true;
+    sendOk(request, context);
+    return null;
   }
 }
index cdce90716e3ba961499c15dca859395c116b1333..09de2c549fdd76dd2813f36515d04de7ce4f2bb8 100644 (file)
@@ -51,8 +51,13 @@ public abstract class RestService extends HttpRequestHandler {
       return false;
     }
 
-    String prefix = "rest";
     String uri = request.uri();
+
+    if (isPrefixlessAllowed() && checkPrefix(uri, getServiceName())) {
+      return true;
+    }
+
+    String prefix = "rest";
     String serviceName = getServiceName();
     int minLength = 1 + prefix.length() + 1 + serviceName.length();
     if (uri.length() >= minLength &&
@@ -70,6 +75,13 @@ public abstract class RestService extends HttpRequestHandler {
     return false;
   }
 
+  /**
+   * Service url must be "/rest/$serviceName", but to preserve backward compatibility, prefixless path could be also supported
+   */
+  protected boolean isPrefixlessAllowed() {
+    return false;
+  }
+
   @NotNull
   /**
    * Use human-readable name or UUID if it is an internal service.
index 03e1830fb5d4ce0422b83a3dd798a958afb54de5..ad0fefcfb786be208b519451a7f73fab2c3a8658 100644 (file)
@@ -25,5 +25,6 @@
     <orderEntry type="library" name="Netty" level="project" />
     <orderEntry type="library" name="http-client" level="project" />
     <orderEntry type="module" module-name="jps-model-impl" scope="TEST" />
+    <orderEntry type="library" name="gson" level="project" />
   </component>
 </module>
\ No newline at end of file
index da3c82f3579cd3888379cd40b94d6afe66b1ed1b..e346ea9c2f296e7b764daca39637aab7ccaf60e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 package com.intellij.platform;
 
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.project.ex.ProjectManagerEx;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.Ref;
-import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vcs.VcsCheckoutProcessor;
+import com.intellij.openapi.vfs.CharsetToolkit;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.projectImport.ProjectSetProcessor;
@@ -30,11 +32,13 @@ import com.intellij.testFramework.LightPlatformTestCase;
 import com.intellij.testFramework.PlatformTestCase;
 import com.intellij.testFramework.PlatformTestUtil;
 import com.intellij.util.containers.ContainerUtil;
-import org.intellij.lang.annotations.Language;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.*;
 
 /**
@@ -52,8 +56,6 @@ public class ProjectSetTest extends LightPlatformTestCase {
   }
 
   public void testProjectSetReader() throws IOException {
-    ProjectSetReader reader = new ProjectSetReader();
-
     final Ref<List<Pair<String, String>>> ref = Ref.create();
     PlatformTestUtil.registerExtension(ProjectSetProcessor.EXTENSION_POINT_NAME, new ProjectSetProcessor() {
       @Override
@@ -67,10 +69,9 @@ public class ProjectSetTest extends LightPlatformTestCase {
       }
     }, myTestRootDisposable);
 
-    @Language("JSON") String descriptor = FileUtil.loadFile(new File(getTestDataPath() + "descriptor.json"));
     ProjectSetProcessor.Context context = new ProjectSetProcessor.Context();
     context.directory = getSourceRoot();
-    reader.readDescriptor(descriptor, context);
+    readDescriptor(new File(getTestDataPath() + "descriptor.json"), context);
 
     List<Pair<String, String>> entries = ref.get();
     assertEquals(2, entries.size());
@@ -96,11 +97,10 @@ public class ProjectSetTest extends LightPlatformTestCase {
       }
     }, myTestRootDisposable);
 
-    @Language("JSON") String descriptor = FileUtil.loadFile(new File(getTestDataPath() + "vcs.json"));
     ProjectSetProcessor.Context context = new ProjectSetProcessor.Context();
     context.directoryName = "newDir";
     context.directory = getSourceRoot();
-    new ProjectSetReader().readDescriptor(descriptor, context);
+    readDescriptor(new File(getTestDataPath() + "vcs.json"), context);
     Collections.sort(pairs, new Comparator<Pair<String, String>>() {
       @Override
       public int compare(@NotNull Pair<String, String> o1, @NotNull Pair<String, String> o2) {
@@ -112,10 +112,9 @@ public class ProjectSetTest extends LightPlatformTestCase {
   }
 
   public void testOpenProject() throws IOException {
-    @Language("JSON") String descriptor = FileUtil.loadFile(new File(getTestDataPath() + "project.json"));
     ProjectSetProcessor.Context context = new ProjectSetProcessor.Context();
     context.directory = VfsUtil.findFileByIoFile(new File(getTestDataPath()), true);
-    new ProjectSetReader().readDescriptor(descriptor, context);
+    readDescriptor(new File(getTestDataPath() + "project.json"), context);
     Project[] projects = ProjectManager.getInstance().getOpenProjects();
     Project project = ContainerUtil.find(projects, new Condition<Project>() {
       @Override
@@ -131,4 +130,16 @@ public class ProjectSetTest extends LightPlatformTestCase {
   public void setUp() throws Exception {
     super.setUp();
   }
+
+  private static void readDescriptor(@NotNull File descriptor, @Nullable ProjectSetProcessor.Context context) throws IOException {
+    InputStreamReader input = new InputStreamReader(new FileInputStream(descriptor), CharsetToolkit.UTF8_CHARSET);
+    JsonElement parse;
+    try {
+      parse = new JsonParser().parse(input);
+    }
+    finally {
+      input.close();
+    }
+    new ProjectSetReader().readDescriptor(parse.getAsJsonObject(), context);
+  }
 }