Commit d7807410 by Suma Shivaprasad

Initial draft

parent 64c78442
...@@ -32,16 +32,7 @@ import org.slf4j.LoggerFactory; ...@@ -32,16 +32,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
...@@ -80,7 +71,6 @@ public class EntityResource { ...@@ -80,7 +71,6 @@ public class EntityResource {
* Submits an entity definition (instance) corresponding to a given type. * Submits an entity definition (instance) corresponding to a given type.
*/ */
@POST @POST
@Path("submit")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response submit(@Context HttpServletRequest request) { public Response submit(@Context HttpServletRequest request) {
...@@ -111,7 +101,7 @@ public class EntityResource { ...@@ -111,7 +101,7 @@ public class EntityResource {
* @param guid GUID for the entity * @param guid GUID for the entity
*/ */
@GET @GET
@Path("definition/{guid}") @Path("{guid}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getEntityDefinition(@PathParam("guid") String guid) { public Response getEntityDefinition(@PathParam("guid") String guid) {
Preconditions.checkNotNull(guid, "Entity GUID cannot be null"); Preconditions.checkNotNull(guid, "Entity GUID cannot be null");
...@@ -157,9 +147,8 @@ public class EntityResource { ...@@ -157,9 +147,8 @@ public class EntityResource {
* @param resultsPerPage number of results for pagination * @param resultsPerPage number of results for pagination
*/ */
@GET @GET
@Path("list/{entityType}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getEntityList(@PathParam("entityType") String entityType, public Response getEntityList(@QueryParam("type") String entityType,
@DefaultValue("0") @QueryParam("offset") Integer offset, @DefaultValue("0") @QueryParam("offset") Integer offset,
@QueryParam("numResults") Integer resultsPerPage) { @QueryParam("numResults") Integer resultsPerPage) {
Preconditions.checkNotNull(entityType, "Entity type cannot be null"); Preconditions.checkNotNull(entityType, "Entity type cannot be null");
...@@ -193,7 +182,7 @@ public class EntityResource { ...@@ -193,7 +182,7 @@ public class EntityResource {
* @return response payload as json * @return response payload as json
*/ */
@PUT @PUT
@Path("update/{guid}") @Path("{guid}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response update(@PathParam("guid") String guid, public Response update(@PathParam("guid") String guid,
@QueryParam("property") String property, @QueryParam("property") String property,
...@@ -223,7 +212,7 @@ public class EntityResource { ...@@ -223,7 +212,7 @@ public class EntityResource {
* @return a list of trait names for the given entity guid * @return a list of trait names for the given entity guid
*/ */
@GET @GET
@Path("traits/list/{guid}") @Path("{guid}/traits")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getTraitNames(@PathParam("guid") String guid) { public Response getTraitNames(@PathParam("guid") String guid) {
Preconditions.checkNotNull(guid, "Entity GUID cannot be null"); Preconditions.checkNotNull(guid, "Entity GUID cannot be null");
...@@ -256,7 +245,7 @@ public class EntityResource { ...@@ -256,7 +245,7 @@ public class EntityResource {
* @param guid globally unique identifier for the entity * @param guid globally unique identifier for the entity
*/ */
@POST @POST
@Path("traits/add/{guid}") @Path("{guid}/traits")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response addTrait(@Context HttpServletRequest request, public Response addTrait(@Context HttpServletRequest request,
...@@ -291,8 +280,8 @@ public class EntityResource { ...@@ -291,8 +280,8 @@ public class EntityResource {
* @param guid globally unique identifier for the entity * @param guid globally unique identifier for the entity
* @param traitName name of the trait * @param traitName name of the trait
*/ */
@PUT @DELETE
@Path("traits/delete/{guid}/{traitName}") @Path("{guid}/traits/{traitName}")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response deleteTrait(@Context HttpServletRequest request, public Response deleteTrait(@Context HttpServletRequest request,
......
...@@ -69,7 +69,7 @@ public class HiveLineageResource { ...@@ -69,7 +69,7 @@ public class HiveLineageResource {
* @param tableName table name * @param tableName table name
*/ */
@GET @GET
@Path("inputs/{tableName}") @Path("table/{tableName}/inputs")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response inputs(@Context HttpServletRequest request, public Response inputs(@Context HttpServletRequest request,
...@@ -103,7 +103,7 @@ public class HiveLineageResource { ...@@ -103,7 +103,7 @@ public class HiveLineageResource {
* @param tableName table name * @param tableName table name
*/ */
@GET @GET
@Path("outputs/{tableName}") @Path("table/{tableName}/outputs")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response outputs(@Context HttpServletRequest request, public Response outputs(@Context HttpServletRequest request,
...@@ -137,7 +137,7 @@ public class HiveLineageResource { ...@@ -137,7 +137,7 @@ public class HiveLineageResource {
* @param tableName table name * @param tableName table name
*/ */
@GET @GET
@Path("schema/{tableName}") @Path("table/{tableName}/schema")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response schema(@Context HttpServletRequest request, public Response schema(@Context HttpServletRequest request,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package org.apache.hadoop.metadata.web.resources; package org.apache.hadoop.metadata.web.resources;
import com.google.common.base.Preconditions;
import org.apache.hadoop.metadata.MetadataException; import org.apache.hadoop.metadata.MetadataException;
import org.apache.hadoop.metadata.MetadataServiceClient; import org.apache.hadoop.metadata.MetadataServiceClient;
import org.apache.hadoop.metadata.services.MetadataService; import org.apache.hadoop.metadata.services.MetadataService;
...@@ -31,13 +32,7 @@ import org.slf4j.LoggerFactory; ...@@ -31,13 +32,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
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.WebApplicationException;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
...@@ -59,6 +54,10 @@ public class TypesResource { ...@@ -59,6 +54,10 @@ public class TypesResource {
private final MetadataService metadataService; private final MetadataService metadataService;
static final String TRAIT = "trait";
static final String CLASS = "class";
static final String STRUCT = "struct";
@Inject @Inject
public TypesResource(MetadataService metadataService) { public TypesResource(MetadataService metadataService) {
this.metadataService = metadataService; this.metadataService = metadataService;
...@@ -69,7 +68,6 @@ public class TypesResource { ...@@ -69,7 +68,6 @@ public class TypesResource {
* domain. Could represent things like Hive Database, Hive Table, etc. * domain. Could represent things like Hive Database, Hive Table, etc.
*/ */
@POST @POST
@Path("submit")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response submit(@Context HttpServletRequest request) { public Response submit(@Context HttpServletRequest request) {
...@@ -97,7 +95,7 @@ public class TypesResource { ...@@ -97,7 +95,7 @@ public class TypesResource {
* @param typeName name of a type which is unique. * @param typeName name of a type which is unique.
*/ */
@GET @GET
@Path("definition/{typeName}") @Path("{typeName}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getDefinition(@Context HttpServletRequest request, public Response getDefinition(@Context HttpServletRequest request,
@PathParam("typeName") String typeName) { @PathParam("typeName") String typeName) {
...@@ -125,7 +123,6 @@ public class TypesResource { ...@@ -125,7 +123,6 @@ public class TypesResource {
* Gets the list of type names registered in the type system. * Gets the list of type names registered in the type system.
*/ */
@GET @GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getTypeNames(@Context HttpServletRequest request) { public Response getTypeNames(@Context HttpServletRequest request) {
try { try {
...@@ -148,15 +145,24 @@ public class TypesResource { ...@@ -148,15 +145,24 @@ public class TypesResource {
* Gets the list of trait type names registered in the type system. * Gets the list of trait type names registered in the type system.
*/ */
@GET @GET
@Path("traits/list")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getTraitNames(@Context HttpServletRequest request) { public Response getTypesByFilter(@Context HttpServletRequest request,
@DefaultValue(TRAIT) @QueryParam("type") String type) {
try { try {
final List<String> traitNamesList = metadataService.getTraitNamesList(); Preconditions.checkNotNull(type, "type cannot be null");
List<String> result = null;
switch(type) {
case TRAIT :
result = metadataService.getTraitNamesList();
case STRUCT :
case CLASS :
//TBD for ÇLASS, STRUCT
throw new UnsupportedOperationException("Unsupported operation on " + type);
}
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
response.put(MetadataServiceClient.RESULTS, new JSONArray(traitNamesList)); response.put(MetadataServiceClient.RESULTS, new JSONArray(result));
response.put(MetadataServiceClient.TOTAL_SIZE, traitNamesList.size()); response.put(MetadataServiceClient.TOTAL_SIZE, result.size());
response.put(MetadataServiceClient.REQUEST_ID, Servlets.getRequestId()); response.put(MetadataServiceClient.REQUEST_ID, Servlets.getRequestId());
return Response.ok(response).build(); return Response.ok(response).build();
......
...@@ -152,7 +152,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -152,7 +152,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
private ClientResponse addProperty(String guid, String property, String value) { private ClientResponse addProperty(String guid, String property, String value) {
WebResource resource = service WebResource resource = service
.path("api/metadata/entities/update") .path("api/metadata/entities")
.path(guid); .path(guid);
return resource.queryParam("property", property).queryParam("value", value) return resource.queryParam("property", property).queryParam("value", value)
...@@ -163,7 +163,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -163,7 +163,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
private ClientResponse getEntityDefinition(String guid) { private ClientResponse getEntityDefinition(String guid) {
WebResource resource = service WebResource resource = service
.path("api/metadata/entities/definition") .path("api/metadata/entities")
.path(guid); .path(guid);
return resource.accept(MediaType.APPLICATION_JSON) return resource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
...@@ -182,7 +182,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -182,7 +182,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
@Test @Test
public void testGetInvalidEntityDefinition() throws Exception { public void testGetInvalidEntityDefinition() throws Exception {
WebResource resource = service WebResource resource = service
.path("api/metadata/entities/definition") .path("api/metadata/entities")
.path("blah"); .path("blah");
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
...@@ -198,8 +198,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -198,8 +198,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
@Test(dependsOnMethods = "testSubmitEntity") @Test(dependsOnMethods = "testSubmitEntity")
public void testGetEntityList() throws Exception { public void testGetEntityList() throws Exception {
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/list/") .path("api/metadata/entities")
.path(TABLE_TYPE) .queryParam("type", TABLE_TYPE)
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.GET, ClientResponse.class); .method(HttpMethod.GET, ClientResponse.class);
...@@ -219,7 +219,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -219,7 +219,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
@Test @Test
public void testGetEntityListForBadEntityType() throws Exception { public void testGetEntityListForBadEntityType() throws Exception {
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/list/blah") .path("api/metadata/entities/blah")
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.GET, ClientResponse.class); .method(HttpMethod.GET, ClientResponse.class);
...@@ -235,7 +235,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -235,7 +235,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
addNewType(); addNewType();
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/list/test") .path("api/metadata/entities/test")
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.GET, ClientResponse.class); .method(HttpMethod.GET, ClientResponse.class);
...@@ -266,8 +266,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -266,8 +266,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
public void testGetTraitNames() throws Exception { public void testGetTraitNames() throws Exception {
final String guid = tableId._getId(); final String guid = tableId._getId();
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/traits/list") .path("api/metadata/entities")
.path(guid) .path(guid)
.path("traits")
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.GET, ClientResponse.class); .method(HttpMethod.GET, ClientResponse.class);
...@@ -299,8 +300,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -299,8 +300,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
final String guid = tableId._getId(); final String guid = tableId._getId();
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/traits/add") .path("api/metadata/entities")
.path(guid) .path(guid)
.path("traits")
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.POST, ClientResponse.class, traitInstanceAsJSON); .method(HttpMethod.POST, ClientResponse.class, traitInstanceAsJSON);
...@@ -328,8 +330,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -328,8 +330,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
LOG.debug("traitInstanceAsJSON = " + traitInstanceAsJSON); LOG.debug("traitInstanceAsJSON = " + traitInstanceAsJSON);
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/traits/add") .path("api/metadata/entities")
.path("random") .path("random")
.path("traits")
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.POST, ClientResponse.class, traitInstanceAsJSON); .method(HttpMethod.POST, ClientResponse.class, traitInstanceAsJSON);
...@@ -343,12 +346,13 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -343,12 +346,13 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
final String guid = tableId._getId(); final String guid = tableId._getId();
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/traits/delete") .path("api/metadata/entities")
.path(guid) .path(guid)
.path("traits")
.path(traitName) .path(traitName)
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.PUT, ClientResponse.class); .method(HttpMethod.DELETE, ClientResponse.class);
Assert.assertEquals(clientResponse.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(clientResponse.getStatus(), Response.Status.OK.getStatusCode());
String responseAsString = clientResponse.getEntity(String.class); String responseAsString = clientResponse.getEntity(String.class);
...@@ -365,12 +369,13 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -365,12 +369,13 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
final String traitName = "blah_trait"; final String traitName = "blah_trait";
ClientResponse clientResponse = service ClientResponse clientResponse = service
.path("api/metadata/entities/traits/delete") .path("api/metadata/entities")
.path("random") .path("random")
.path("traits")
.path(traitName) .path(traitName)
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.PUT, ClientResponse.class); .method(HttpMethod.DELETE, ClientResponse.class);
Assert.assertEquals(clientResponse.getStatus(), Assert.assertEquals(clientResponse.getStatus(),
Response.Status.BAD_REQUEST.getStatusCode()); Response.Status.BAD_REQUEST.getStatusCode());
} }
......
...@@ -70,7 +70,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT { ...@@ -70,7 +70,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
System.out.println("typesAsJSON = " + typesAsJSON); System.out.println("typesAsJSON = " + typesAsJSON);
WebResource resource = service WebResource resource = service
.path("api/metadata/types/submit"); .path("api/metadata/types");
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
...@@ -93,7 +93,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT { ...@@ -93,7 +93,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
System.out.println("typeName = " + typeDefinition.typeName); System.out.println("typeName = " + typeDefinition.typeName);
WebResource resource = service WebResource resource = service
.path("api/metadata/types/definition") .path("api/metadata/types")
.path(typeDefinition.typeName); .path(typeDefinition.typeName);
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
...@@ -114,7 +114,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT { ...@@ -114,7 +114,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
@Test @Test
public void testGetDefinitionForNonexistentType() throws Exception { public void testGetDefinitionForNonexistentType() throws Exception {
WebResource resource = service WebResource resource = service
.path("api/metadata/types/definition") .path("api/metadata/types")
.path("blah"); .path("blah");
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
...@@ -127,7 +127,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT { ...@@ -127,7 +127,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
@Test(dependsOnMethods = "testSubmit") @Test(dependsOnMethods = "testSubmit")
public void testGetTypeNames() throws Exception { public void testGetTypeNames() throws Exception {
WebResource resource = service WebResource resource = service
.path("api/metadata/types/list"); .path("api/metadata/types");
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
...@@ -150,9 +150,10 @@ public class TypesJerseyResourceIT extends BaseResourceIT { ...@@ -150,9 +150,10 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
String[] traitsAdded = addTraits(); String[] traitsAdded = addTraits();
WebResource resource = service WebResource resource = service
.path("api/metadata/types/traits/list"); .path("api/metadata/types");
ClientResponse clientResponse = resource ClientResponse clientResponse = resource
.queryParam("type", TypesResource.TRAIT)
.accept(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON)
.method(HttpMethod.GET, ClientResponse.class); .method(HttpMethod.GET, ClientResponse.class);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment