REST API: The Plastic SCM API


Introduction

Welcome to the Plastic SCM client REST API documentation!

This API allows programmers to perform Plastic client operations in their machine, provided that a Plastic client is already in place and configured. It's implemented in a REST fashion, meaning that virtually any language can interact with it through HTTP connections. Each of the supported Plastic client operations is exposed using a different URI and HTTP method. You can have a look at the detailed endpoint documentation for further information.


How to use the API

Executing commands through the Plastic REST API is extremely simple: using your programming language of choice, you just need to perform an HTTP request against the appropriate URI, setting the HTTP method matching your targeted endpoint and including the request body format stated by said endpoint. You can have a look to our API client examples in the following sections or to our advanced API examples if you need a starting point!


Starting the local API

The API requests are received by a simple, standalone HTTP server, exposed by the plasticapi.exe command. You can find it in the Plastic SCM client directory (C:\Program Files\PlasticSCM5\client by default on Windows, and /opt/plasticscm5/client on Linux).

Note: The user account running the plasticapi.exe command must have a valid Plastic SCM configuration. You can issue a cm checkconnection from a command line shell to see if the user has a valid configuration.

Windows configuration

On windows machines, we need to perform an extra configuration step prior to starting the plasticapi.exe from a non-administration account.

We must tell Windows to grant permissions to plasticapi.exe http server to listen on a particular URL depending on if we're going to expose the API only for local machine requests or remote requests also. For this purpose, we must use the netsh tool using an administration account as the following examples show:

  • Accept remote requests:
    • If we want to enable lewis account to run plasticapi.exe to accept remote requests on port 9090 (the default one for plasticapi.exe), the command is: netsh http add urlacl url=http://+:9090/ user=lewis
    • Otherwise, if we want to enable any user to run plasticapi.exe to accept remote requests on port 9090, the command is: netsh http add urlacl url=http://+:9090/ user=everyone
  • Accept local requests:
    • Allow user account katy to run plasticapi.exe to accept local requests on port 9090 (the default one for plasticapi.exe): netsh http add urlacl url=http://localhost:9090/ user=katy
    • Otherwise, if we want to enable any user to run plasticapi.exe to accept local requests on port 9090, the command is: netsh http add urlacl url=http://localhost:9090/ user=everyone
  • Note: The configuration to accept local requests is not mandatory unless you want to configure plasticapi.exe to allow listening local and remote requests at the same time.

Starting the API

Once the configuration steps described above are completed, you can execute the plasticapi http server application in PowerShell or cmd terminal by running:

plasticapi [params]

The available parameters are:

  • -p <port-number> - Tells the server to listen on port <port-number> instead of 9090.
  • -r - Allows remote connection (for example, coming from a different host).

Examples:

  • Accept local requests on default port (9090): plasticapi
  • Accept local requests on port 65000: plasticapi -p 65000
  • Accept remote requests on default port (9090): plasticapi -r
  • Accept remote requests on port 65000: plasticapi -p 65000 -r
In all cases, the following message indicates the successful launch of the plasticapi: Plastic SCM client REST API: Press <ENTER> to exit.

Configuration and examples summary

See below some simple configuration and launching steps for plasticapi on Windows:

  1. Standard user (lewis) + listen in any host:
    • Run as administrator: netsh http add urlacl url=http://+:9090/ user=lewis
    • Run as lewis: plasticapi -p 9090 -r
  2. Standard user (lewis) + listen only in localhost
    • Run as administrator: netsh http add urlacl url=http://localhost:9090/ user=lewis
    • Run as lewis: plasticapi -p 9090

Troubleshooting

If the plasticapi does not start, the following message displays:

Unable to start the REST API listener. Please make sure that you have HTTP permissions for the current port and user.

The most common issues (with solutions) that prevent the start of the plasticapi are:

  1. Plastic SCM user not configured:

    The user running plasticapi.exe (plasticapi on XNIX) must have a valid Plastic SCM client configuration with the Plastic SCM server. To validate this requirement, open a command line shell from the same user account running plasticapi.exe and type cm checkconnection.

    If the command output shows Test connection executed successfully, the Plastic SCM configuration is valid. Otherwise, follow the instructions shown by the command output.

  2. plasticapi http server cannot listen on base API URL:

    This issue occurs only on Windows with non-admin user accounts. To correct this issue, you can use the netsh command to grant access on a specific URL, but the base URL for Plastic API has to match exactly with the rule specified in the netsh http add urlacl command.

    This means that if you just issued the following netsh command

    netsh http add urlacl url=http://+:9090/ user=lewis

    ...then, the plasticapi won't be able to start by only for listening in localhost for the lewis standard user account:

    plasticapi -p 9090 Unable to start the REST API listener. Please make sure that you have HTTP permissions for the current port and user.

    You will need to also add the localhost rule by typing:

    netsh http add urlacl url=http://localhost:9090/ user=lewis

    Another potential issue is an user mismatch between the one configured in the netsh command and the one running the plasticapi application. The recommendation is to use full domain user spec for netsh user param. To prevent this issue, type first a whoami command from the same account you're going to use to run plasticapi, pick the output, and use it for the netsh command. For example: whoami codice\carl netsh http add urlacl url=http://+:9090/ user=codice\carl

Remember to use an admin account shell to run netsh commands.

Finally, to list the netsh configuration, you can issue the following netsh command and check the granted base URL's for specific usernames:

netsh http show urlacl

The output and explanation below:

... Reserved URL : http://localhost:15001/ User: \Everyone Listen: Yes Delegate: No SDDL: D:(A;;GX;;;WD) Reserved URL : http://+:25000/ User: \Everyone Listen: No Delegate: No SDDL: D:(D;;GA;;;WD) Reserved URL : http://+:9090/ User: BLACKMORE\greg Listen: Yes Delegate: No SDDL: D:(A;;GX;;;S-##-##-##############) Reserved URL : http://localhost:9090/ User: BLACKMORE\greg Listen: Yes Delegate: No SDDL: D:(A;;GX;;;S-##-##-##############)

The four entries shown above tell us the following:

  1. Any user can launch plasticapi to accept only localhost requests on port 15001 (plasticapi -p 15001).
  2. No one user can launch plasticapi to accept remote requests on port 25000 (Listen: NO).
  3. The user blackmore\greg can launch plasticapi to accept remote requests in default port 9090: plasticapi -r plasticapi -p 9090 -r
  4. The user blackmore\greg can launch plasticapi to accept localhost requests in default port 9090: plasticapi plasticapi -p 9090

Note that, if the third entry doesn't exist (Reserved URL: http://+:9090/), the user blackmore\greg won't be able to use the -r flag in the plasticapi command to accept remote requests.

On the other hand, if third entry exists but fourth doesn't, the user blackmore\greg won't be able to run plasticapi to listen for local requests, and has to forcibly use the -r flag with the plasticapi command.


Java example

Program

package com.codice.examples;


import com.codice.examples.actions.CreateRepositoryAction;
import com.codice.examples.actions.RenameAction;
import com.codice.examples.models.Repository;
import retrofit.Callback;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.client.Response;

import java.util.List;
import java.util.Scanner;

public class Program {

    static IApi apiClient;
    static Scanner in;
    static String instructions = "1 - List repositories (cm lrep)\n" +
    "2 - Create a repository (cm mkrep)\n" +
    "3 - Rename a repository (cm rnrep)\n" +
    "4 - Delete a repository (cm rmrep)\n" +
    "\n"                                   +
    "Select an option (1/2/3/4): ";

    static void initApiClient() {
        RestAdapter adapter = new RestAdapter.Builder()
                .setEndpoint("http://localhost:9090")
                .build();

        apiClient = adapter.create(IApi.class);
    }

    static void listRepositories() {
        List<Repository> repositories = apiClient.getRepositories();
        for(Repository repo : repositories) {
            System.out.println(String.format("%d_%d %s %s",
                    repo.getRepId().getId(),
                    repo.getRepId().getModuleId(),
                    repo.getName(),
                    repo.getServer()));
        }
    }

    static void createRepository() {
        System.out.print("Write a name for the repository: ");
        String name = in.nextLine();
        System.out.print("Write the direction of the server: ");
        String server = in.nextLine();

        CreateRepositoryAction action =
                new CreateRepositoryAction(name, server);

        Repository newRepo = apiClient.createRepository(action);
        System.out.println(String.format(
                "Repository %s successfully created!", newRepo.getName()));
    }

    static void renameRepository() {
        listRepositories();
        System.out.print("Write the name of the repository to rename: ");
        String repository = in.nextLine();
        System.out.print(String.format("Write a new name for %s: ", repository));
        String newName = in.nextLine();

        RenameAction action = new RenameAction(newName);
        Repository renamedRepo =
                apiClient.renameRepository(repository, action);
        System.out.println(
                String.format("Repository %s successfully renamed to %s.",
                        repository, renamedRepo.getName())
        );
    }

    static void deleteRepository() {
        listRepositories();
        System.out.print("Write the name of the repository to delete: ");
        String repository = in.nextLine();

        apiClient.deleteRepository(repository, new Callback<Void>() {
            @Override
            public void success(Void aVoid, Response response) {
                // Retrofit limitation. Here, we should only check the
                // status code.
                System.out.println("Repository successfully deleted!");
            }

            @Override
            public void failure(RetrofitError retrofitError) {

            }
        });
    }

    public static void main(String[] args) {
        initApiClient();

        System.out.print(instructions);
        in = new Scanner(System.in);
        String optionStr = in.nextLine();
        int option;

        try {
            option = Integer.valueOf(optionStr);
        } catch (NumberFormatException e) {
            option = 1;
        }

        switch (option) {
            case 1:
            default:
                listRepositories();
                break;
            case 2:
                createRepository();
                break;
            case 3:
                renameRepository();
                break;
            case 4:
                deleteRepository();
                break;
        }

    }
}

Actions

package com.codice.examples.actions;


import com.sun.istack.internal.NotNull;

public class CreateRepositoryAction {

    private String name;
    private String server;

    public CreateRepositoryAction(@NotNull String name,
                                  @NotNull String server) {
        this.name = name;
        this.server = server;
    }
}

package com.codice.examples.actions;


import com.sun.istack.internal.NotNull;

public class RenameAction {

    private String name;

    public RenameAction(@NotNull String name) {
        this.name = name;
    }
}

Models

package com.codice.examples.models;


public class Owner {

    private String name;
    private boolean isGroup;

    public String getName() { return name; }
    public boolean isGroup() { return isGroup; }
}

package com.codice.examples.models;


public class RepId {

    private int id;
    private int moduleId;

    public int getId() { return id; }
    public int getModuleId() { return moduleId; }
}

package com.codice.examples.models;


import java.util.UUID;

public class Repository {

    private RepId repId;
    private Owner owner;
    private String name;
    private UUID guid;
    private String server;

    public RepId getRepId() { return repId; }
    public Owner getOwner() { return owner; }
    public String getName() { return name; }
    public UUID getGuid() { return guid; }
    public String getServer() { return server; }
}

IApi

package com.codice.examples;


import com.codice.examples.actions.CreateRepositoryAction;
import com.codice.examples.actions.RenameAction;
import com.codice.examples.models.Repository;
import retrofit.Callback;
import retrofit.http.*;

import java.util.List;

public interface IApi {

    @GET("/api/v1/repos")
    List<Repository> getRepositories();

    @POST("/api/v1/repos")
    Repository createRepository(@Body CreateRepositoryAction params);

    @PUT("/api/v1/repos/{repname}")
    Repository renameRepository(@Path("repname") String repositoryName,
                                @Body RenameAction params);

    @DELETE("/api/v1/repos/{repname}")
    void deleteRepository(@Path("repname") String repositoryName,
                          Callback<Void> callback); // Retrofit limitation.
}

C# example

Program

using Csharp_examples.Actions;
using Csharp_examples.Models;
using Csharp_examples.Utils;
using System;
using System.Collections.Generic;

namespace Csharp_examples
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write(mActions);
            string optionStr = Console.ReadLine();

            int option;
            Int32.TryParse(optionStr, out option);

            switch (option)
            {
                case 1:
                default:
                    ListRepositories();
                    break;
                case 2:
                    CreateRepository();
                    break;
                case 3:
                    RenameRepository();
                    break;
                case 4:
                    DeleteRepository();
                    break;
            }
        }

        static void ListRepositories()
        {
            List<Repository> repositories = ApiUtils.ReadRepositoryList().Result;

            foreach (Repository repo in repositories)
            {
                Console.WriteLine(string.Format(" {0}_{1} {2} {3}",
                    repo.RepId.Id, repo.RepId.ModuleId, repo.Name, repo.Server));
            }
        }

        static void CreateRepository()
        {
            Console.Write("Write a name for the repository: ");
            string name = Console.ReadLine();
            Console.Write("Write the direction of the server: ");
            string server = Console.ReadLine();

            CreateRepository action = new CreateRepository()
            {
                Name = name,
                Server = server
            };

            Repository newRepo = ApiUtils.CreateRepository(action).Result;
            Console.Write(string.Format("Repository {0} successfully created!", newRepo.Name));
        }

        static void RenameRepository()
        {
            ListRepositories();
            Console.Write("Write the name of the repository to rename: ");
            string repository = Console.ReadLine();
            Console.Write(string.Format("Write a new name for {0}: ", repository));
            string newName = Console.ReadLine();

            Rename action = new Rename()
            {
                Name = newName
            };

            Repository renamedRepo = ApiUtils.RenameRepository(repository, action).Result;
            Console.Write(string.Format("Repository {0} successfully renamed to {1}.",
                repository, renamedRepo.Name));
        }

        static void DeleteRepository()
        {
            ListRepositories();
            Console.Write("Write the name of the repository to delete: ");
            string repository = Console.ReadLine();

            ApiUtils.DeleteRepository(repository).Wait();
            Console.Write(string.Format("Repository {0} successfully deleted!", repository));
        }

        static string mActions = @"1 - List repositories (cm lrep)
2 - Create a repository (cm mkrep)
3 - Rename a repository (cm rnrep)
4 - Delete a repository (cm rmrep)

Select an option (1/2/3/4): ";
    }
}

Actions

namespace Csharp_examples.Actions
{
    public class CreateRepository
    {
        public string Name { get; set; }
        public string Server { get; set; }
    }
}

namespace Csharp_examples.Actions
{
    public class Rename
    {
        public string Name { get; set; }
    }
}

Models

namespace Csharp_examples.Models
{
    public class Owner
    {
        public string Name { get; set; }
        public bool IsGroup { get; set; }
    }
}

namespace Csharp_examples.Models
{
   public class RepId
    {
        public int Id { get; set; }
        public int ModuleId { get; set; }
    }
}

using System;

namespace Csharp_examples.Models
{
    public class Repository
    {
        public RepId RepId { get; set; }
        public Owner Owner { get; set; }
        public string Name { get; set; }
        public Guid Guid { get; set; }
        public string Server { get; set; }
    }
}

Utils

using Csharp_examples.Actions;
using Csharp_examples.Models;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace Csharp_examples.Utils
{
    public class ApiUtils
    {
        static HttpClient GetHttpClient()
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri("http://localhost:9090/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            return client;
        }

        public static async Task<List<Repository>> ReadRepositoryList()
        {
            HttpClient client = GetHttpClient();
            using (client)
            {
                HttpResponseMessage response = await client.GetAsync("api/v1/repos");

                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                    throw new Exception(string.Format(
                        "API Status Code {0}. Expected OK.", response.StatusCode));

                return await response.Content.ReadAsAsync<List<Repository>>();
            }
        }

        public static async Task<Repository> CreateRepository(CreateRepository createParams)
        {
            HttpClient client = GetHttpClient();
            using (client)
            {
                HttpResponseMessage response =
                    await client.PostAsJsonAsync("api/v1/repos", createParams);

                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                    throw new Exception(string.Format(
                        "API Status Code {0}. Expected OK.", response.StatusCode));

                return await response.Content.ReadAsAsync<Repository>();
            }
        }

        public static async Task<Repository> RenameRepository(string repositoryName, Rename renameParam)
        {
            HttpClient client = GetHttpClient();
            string uri = string.Format("api/v1/repos/{0}", repositoryName);
            using (client)
            {
                HttpResponseMessage response =
                    await client.PutAsJsonAsync(uri, renameParam);

                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                    throw new Exception(string.Format(
                        "API Status Code {0}. Expected OK.", response.StatusCode));

                return await response.Content.ReadAsAsync<Repository>();
            }
        }

        public static async Task DeleteRepository(string repositoryName)
        {
            HttpClient client = GetHttpClient();
            string uri = string.Format("api/v1/repos/{0}", repositoryName);
            using (client)
            {
                HttpResponseMessage response =
                    await client.DeleteAsync(uri);

                if (response.StatusCode != System.Net.HttpStatusCode.NoContent)
                    throw new Exception(string.Format(
                        "API Status Code {0}. Expected NoContent.", response.StatusCode));
            }
        }
    }
}

Python example

cm lrep

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_repositories(repositories):
    line = "{0} {1} {2}"
    for repo in repositories:
        print(line.format(repo["repId"]["id"],
                          repo["name"],
                          repo["server"]))


def get_repositories():
    url = __api_url + "/repos"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("The repository list could not be retrieved.", file=sys.stderr)
    exit(-1)


def main():
    try:
        print_repositories(get_repositories())
    except requests.RequestException as e:
        print(e)
        exit(-1)


if __name__ == "__main__":
    main()

cm mkrep

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def create_repository(name, server):
    url = __api_url + "/repos"
    params = {"name": name,
              "server": server}
    req = requests.post(url, params)
    if req.status_code is not 200:
        print("The repository could not be created", file=sys.stderr)
        exit(-1)


def main():
    name = input("Write a name for the new repository: ")
    server = input("Enter the PlasticSCM server address: ")
    try:
        create_repository(name, server)
    except requests.RequestException as e:
        print(e)
        exit(-1)

if __name__ == "__main__":
    main()

cm rnrep

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_repositories(repositories):
    line = "{0} {1} {2}"
    for repo in repositories:
        print(line.format(repo["repId"]["id"],
                          repo["name"],
                          repo["server"]))


def get_repositories():
    url = __api_url + "/repos"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("The repository list could not be retrieved.", file=sys.stderr)
    exit(-1)


def rename_repo(old_name, new_name):
    url = __api_url + "/repos/" + old_name
    params = {"name": new_name}
    req = requests.put(url, params)
    if req.status_code is not 200:
        print("The repository could not be created.", file=sys.stderr)
        exit(-1)


def main():
    try:
        print_repositories(get_repositories())
        old_name = input("\nWrite the name of the repository to rename: ")
        new_name = input("Write a new name for the repository " + old_name + ": ")
        rename_repo(old_name, new_name)
        print_repositories(get_repositories())
    except requests.RequestException as e:
        print(e, file=sys.stderr)
        exit(-1)

if __name__ == "__main__":
    main()

cm rmrep

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_repositories(repositories):
    line = "{0} {1} {2}"
    for repo in repositories:
        print(line.format(repo["repId"]["id"],
                          repo["name"],
                          repo["server"]))


def get_repositories():
    url = __api_url + "/repos"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("The repository list could not be retrieved.", file=sys.stderr)
    exit(-1)


def remove_repo(name):
    url = __api_url + "/repos/" + name
    req = requests.delete(url)
    if req.status_code is not 204:
        print("The repository could not be deleted", file=sys.stderr)
        exit(-1)


def main():
    try:
        print_repositories(get_repositories())
        repo = input("Write the name of the repository to delete: ")
        remove_repo(repo)
        print_repositories(get_repositories())
    except requests.RequestException as e:
        print(e, file=sys.stderr)
        exit(-1)

if __name__ == "__main__":
    main()

cm lwk

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_workspaces(wkspaces):
    line = "Name: {0}, path: {1}"
    for wkspace in wkspaces:
        print(line.format(wkspace["name"],
                          wkspace["path"]))


def get_workspaces():
    url = __api_url + "/wkspaces"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("Workspace list could not be retrieved.", file=sys.stderr)
    exit(-1)


def main():
    try:
        print_workspaces(get_workspaces())
    except requests.RequestException as e:
        print(e, file=sys.stderr)
        exit(-1)

if __name__ == "__main__":
    main()

cm mkwk

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_repositories(repositories):
    line = "{0} {1} {2}"
    for repo in repositories:
        print(line.format(repo["repId"]["id"],
                          repo["name"],
                          repo["server"]))


def get_repositories():
    url = __api_url + "/repos"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("The repository list could not be retrieved.", file=sys.stderr)
    exit(-1)


def create_workspace(name, path, repository):
    url = __api_url + "/wkspaces"
    params = {"name": name,
              "path": path,
              "repository": repository}
    req = requests.post(url, params)
    if req.status_code is not 200:
        print("The workspace could not be created.", file=sys.stderr)
        exit(-1)


def main():
    try:
        print_repositories(get_repositories())
        repository = input("Write the name of a repository to create a new workspace to: ")
        name = input("Write the name for the new workspace: ")
        path = input("Write the path for the new workspace: ")
        create_workspace(name, path, repository)
    except requests.RequestException as e:
        print(e, file=sys.stderr)
        exit(-1)


if __name__ == "__main__":
    main()

cm rmwk

import sys
import requests

__api_url = "http://localhost:9090/api/v1"


def print_workspaces(wkspaces):
    line = "Name: {0}, path: {1}"
    for wkspace in wkspaces:
        print(line.format(wkspace["name"],
                          wkspace["path"]))


def get_workspaces():
    url = __api_url + "/wkspaces"
    req = requests.get(url)
    if req.status_code is 200:
        return req.json()
    print("Workspace list could not be retrieved.", file=sys.stderr)
    exit(-1)


def remove_workspace(name):
    url = __api_url + "/wkspaces/" + name
    req = requests.delete(url)
    if req.status_code is not 204:
        print("The workspace could not be deleted.", file=sys.stderr)
        sys.exit(-1)


def main():
    try:
        print_workspaces(get_workspaces())
        name = input("Write the name of the workspace to delete: ")
        remove_workspace(name)
    except requests.RequestException as e:
        print(e, file=sys.stderr)
        exit(-1)


if __name__ == "__main__":
    main()

API Reference

Repositories

List all repositories

GET /api/v1/repos

Response

Status: 200 OK

[
    {
        "repId":
        {
            "id": 1,
            "moduleId": 0
        },
        "name": "default",
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner":
        {
            "name": "all",
            "isGroup": false
        },
        "server": "localhost:8084"
    }
]

Get a single repository

GET /api/v1/repos/:repname

Response

Status: 200 OK

{
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "default",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
}

Create a repository

POST /api/v1/repos

Parameters

Name Type Description
name string Required. The name of the new repository.
server string The target server where the repository should be created.

Example

{
    "name": "repName",
    "server": "myserver:8084"
}

Response

The repository data will be returned once the operation is complete.

Status: 200 OK

{
    "repId":
    {
        "id": 2,
        "moduleId": 0
    },
    "name": "repName",
    "guid": "c45b8af3-1a10-d31f-baca-c00590d12456",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "myserver:8084"
}

Rename a repository

PUT /api/v1/repos/:repname

Parameters

Name Type Description
name string Required. The new name for the repository.

Example

{
    "name": "newName"
}

Response

The updated repository data will be returned once the operation is complete.

Status: 200 OK

{
    "repId":
    {
        "id": 2,
        "moduleId": 0
    },
    "name": "newName",
    "guid": "c45b8af3-1a10-d31f-baca-c00590d12456",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "myserver:8084"
}

Remove a repository

DELETE /api/v1/repos/:repname

Response

Status: 204 No Content


Workspaces

List all workspaces

GET /api/v1/wkspaces

Response

Status: 200 OK

[
    {
        "name": "my_wk",
        "guid": "e93518f8-20a6-4534-a7c6-f3d9ebac45cb",
        "path": "c:\\path\\to\\workspace",
        "machineName": "MACHINE"
    }
]

Get a single workspace

GET /api/v1/wkspaces/:wkname

Response

Status: 200 OK

{
    "name": "my_wk",
    "guid": "e93518f8-20a6-4534-a7c6-f3d9ebac45cb",
    "path": "c:\\path\\to\\workspace",
    "machineName": "MACHINE"
}

Create a workspace

POST /api/v1/wkspaces

Parameters

Name Type Description
name string Required. The name of the new workspace.
path string Required. The target path where the repository should be created.
repository string The source repository for the new workspace.

Example

{
    "name": "new_wk",
}

Response

The workspace data will be returned once the operation is complete.

Status: 200 OK

{
    "name": "new_wk"
    "guid": "ed3248d2-5591-407a-94e6-84dac0ce015b"
    "path": "c:\\the\\path\\to\\new_wk"
    "machineName": "MACHINE"
}

Rename a workspace

PUT /api/v1/wkspaces/:wkname

Parameters

Name Type Description
name string Required. The new name for the workspace.

Example

{
    "name": "newName"
}

Response

The updated workspace data will be returned once the operation is complete.

Status: 200 OK

{
    "name": "newName"
    "guid": "ed3248d2-5591-407a-94e6-84dac0ce015b"
    "path": "c:\\the\\path\\to\\new_wk"
    "machineName": "MACHINE"
}

Remove a workspace

DELETE /api/v1/wkspaces/:wkname

Response

Status: 204 No Content


Branches

List all branches

GET /api/v1/repos/:repname/branches

Parameters

Name Type Description
q string An optional constraints string using the cm find command syntax.

Example

GET http://localhost:9090/api/v1/repos/myrepo/branches?q=id > 50

Response

Status: 200 OK

[
    {
        "name": "/main/task001/task002",
        "id": 1388,
        "parentId": 1067,
        "lastChangeset": 1383,
        "comment": "Testing: implement new smoke tests.",
        "creationDate": "2015-06-30T15:18:08",
        "guid": "0ced86fe-37cb-4801-8f6d-0081edb46d39",
        "owner": {
            "name": "codice-master",
            "isGroup": false
        },
        "repository": {
            "name": "mainrepo",
            "repId": {
                "id": 1,
                "moduleId": 0
            },
            "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
            "owner": {
                "name": "all",
                "isGroup": false
            },
            "server": "localhost:8084"
        }
    }
]

Get a single branch

GET /api/v1/repos/:repname/branches/:branchname

Please note that branch names are hierarchical.

Request example

GET /api/v1/repos/mainrepo/branches/main/task001/task002

Response

Status: 200 OK

{
    "name": "/main/task001/task002",
    "id": 1388,
    "parentId": 1067,
    "lastChangeset": 1383,
    "comment": "Testing: implement new smoke tests.",
    "creationDate": "2015-06-30T15:18:08",
    "guid": "0ced86fe-37cb-4801-8f6d-0081edb46d39",
    "owner": {
        "name": "codice-master",
        "isGroup": false
    },
    "repository": {
        "name": "mainrepo",
        "repId": {
            "id": 1,
            "moduleId": 0
        },
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner": {
            "name": "all",
            "isGroup": false
        },
        "server": "localhost:8084"
    }
}

Create a branch

POST /api/v1/repos/:repname/branches

Parameters

Name Type Description
name string Required. The branch name. Do not use hierarchy here.
originType string Required. The type of source to retrieve the parent changeset of the new branch. It can be changeset, label or branch. See examples for further detail.
origin string Required. The point of origin from which the branch will be created.
topLevel boolean Whether or not the branch will be top-level (i.e. it will have no parent). Set to false by default.

Examples

{
    "name": "newBranch",
    "originType": "branch",
    "origin": "/main/scm003",
    "topLevel": false
}
{
    "name": "newBranch",
    "originType": "label",
    "origin": "BL000",
    "topLevel": true
}
{
    "name": "newBranch",
    "originType": "changeset",
    "origin": "97",
    "topLevel": false
}

Response

The created branch info will be returned once the operation is complete.

Status: 200 OK

{
  "name": "/main/scm003/newBranch",
  "id": 1395,
  "parentId": 1067,
  "lastChangeset": 1383,
  "creationDate": "2015-07-01T09:01:08",
  "guid": "3416a2b4-f88a-43b1-8319-3424bc23c77b",
  "owner": {
    "name": "tester",
    "isGroup": false
  },
  "repository": {
    "name": "default",
    "repId": {
      "id": 1,
      "moduleId": 0
    },
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Rename a branch

PATCH /api/v1/repos/:repname/branches/:branchname

Parameters

Name Type Description
name string Required. The new name for the branch. Please have in mind that branch hierarchy cannot be changed.

Example

{
    "name": "/main/task001/task003"
}

Response

The updated branch info will be returned once the operation is complete.

Status: 200 OK

{
    "name": "/main/task001/task003",
    "id": 1388,
    "parentId": 1067,
    "lastChangeset": 1383,
    "comment": "Testing: implement new smoke tests.",
    "creationDate": "2015-06-30T15:18:08",
    "guid": "0ced86fe-37cb-4801-8f6d-0081edb46d39",
    "owner": {
        "name": "codice-master",
        "isGroup": false
    },
    "repository": {
        "name": "mainrepo",
        "repId": {
            "id": 1,
            "moduleId": 0
        },
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner": {
            "name": "all",
            "isGroup": false
        },
        "server": "localhost:8084"
    }
}

Remove a branch

Only empty branches with no children can be removed.

DELETE /api/v1/repos/:repname/branches/:branchname

Response

Status: 204 No Content


Labels

List all labels

GET /api/v1/repos/:repname/labels

Parameters

Name Type Description
q string An optional constraints string using the cm find command syntax.

Example

GET http://localhost:9090/api/v1/repos/myrepo/labels?q=date > '2015-07-01'

Response

Status: 200 OK

[
  {
    "name": "BL000",
    "id": 1391,
    "changeset": 100,
    "comment": "",
    "creationDate": "2015-07-01T07:45:56",
    "branch": {
      "name": "/main",
      "id": 3,
      "parentId": -1,
      "lastChangeset": 101,
      "comment": "main branch",
      "creationDate": "2015-04-09T07:17:00",
      "guid": "5fc2d7c8-05e1-4987-9dd9-74eaec7c27eb",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "repository": {
        "name": "default",
        "repId": {
          "id": 1,
          "moduleId": 0
        },
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner": {
          "name": "all",
          "isGroup": false
        },
        "server": "localhost:8084"
      }
    },
    "owner": {
      "name": "tester",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  }
]

Get a single label

GET /api/v1/repos/:repname/labels/:labelname

Response

Status: 200 OK

{
  "name": "BL000",
  "id": 1391,
  "changeset": 100,
  "branch": {
    "name": "/main",
    "id": 3,
    "parentId": -1,
    "lastChangeset": 101,
    "comment": "main branch",
    "creationDate": "2015-04-09T07:17:00",
    "guid": "5fc2d7c8-05e1-4987-9dd9-74eaec7c27eb",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  },
  "comment": "",
  "creationDate": "2015-07-01T07:45:56",
  "owner": {
    "name": "tester",
    "isGroup": false
  }
  "repository": {
    "name": "default",
    "repId": {
      "id": 1,
      "moduleId": 0
    },
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Create a label

POST /api/v1/repos/:repname/labels/

Parameters

Name Type Description
name string Required. The new name for the label.
changeset integer Required. The changeset to label.
comment string The comment to add to the label.
applyToXlinks boolean If true, all xlinked changesets present in the specified changeset tree will be labelled as well. Set to false by default.

Example

{
    "name": "BL001",
    "changeset": 99,
    "comment": "Stable baseline - 1",
    "applyToXlinks": false
}

Response

The created label info will be returned once the operation is complete.

Status: 200 OK

{
  "name": "BL001",
  "id": 1401,
  "changeset": 99,
  "comment": "Stable baseline - 1",
  "creationDate": "2015-07-01T10:19:14",
  "branch": {
    "name": "/main",
    "id": 3,
    "parentId": -1,
    "lastChangeset": 101,
    "comment": "main branch",
    "creationDate": "2015-04-09T07:17:00",
    "guid": "5fc2d7c8-05e1-4987-9dd9-74eaec7c27eb",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  },
  "owner": {
    "name": "tester",
    "isGroup": false
  },
  "repository": {
    "name": "default",
    "repId": {
      "id": 1,
      "moduleId": 0
    },
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Rename a label

PATCH /api/v1/repos/:repname/labels/:labelname

Parameters

Name Type Description
name string Required. The new name for the label.

Example

{
    "name": "v1.0.0",
}

Response

The updated label info will be returned once the operation is complete.

Status: 200 OK

{
  "name": "v1.0.0",
  "id": 1401,
  "changeset": 99,
  "comment": "Stable baseline - 1",
  "creationDate": "2015-07-01T10:19:14",
  "branch": {
    "name": "/main",
    "id": 3,
    "parentId": -1,
    "lastChangeset": 101,
    "comment": "main branch",
    "creationDate": "2015-04-09T07:17:00",
    "guid": "5fc2d7c8-05e1-4987-9dd9-74eaec7c27eb",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  },
  "owner": {
    "name": "tester",
    "isGroup": false
  },
  "repository": {
    "name": "default",
    "repId": {
      "id": 1,
      "moduleId": 0
    },
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Remove a label

DELETE /api/v1/repos/:repname/labels/:labelname

Response

Status: 204 No Content


Changesets

List all changesets

GET /api/v1/repos/:repname/changesets

Parameters

Name Type Description
q string An optional constraints string using the cm find command syntax.

Example

GET http://localhost:9090/api/v1/repos/myrepo/changesets?q=date > '2015-07-01'

Response

Status: 200 OK

[
  {
    "id": 0,
    "parentId": -1,
    "comment": "Root dir",
    "creationDate": "2015-04-09T07:17:00",
    "guid": "0497ef04-4c81-4090-8458-649885400c84",
    "branch": {
      "name": "\/main",
      "id": 3,
      "parentId": -1,
      "lastChangeset": 101,
      "comment": "main branch",
      "creationDate": "2015-04-09T07:17:00",
      "guid": "5fc2d7c8-05e1-4987-9dd9-74eaec7c27eb",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "repository": {
        "name": "default",
        "repId": {
          "id": 1,
          "moduleId": 0
        },
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner": {
          "name": "all",
          "isGroup": false
        },
        "server": "localhost:8084"
      }
    },
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  },
  ...
]

List all changesets by branch

GET /api/v1/repos/:repname/branches/:branchname/changesets

Parameters

Name Type Description
q string An optional constraints string using the cm find command syntax.

Example

GET http://localhost:9090/api/v1/repos/myrepo/branches/main/changesets?q=changesetid > 50

Response

Status: 200 OK

[
  {
    "id": 77,
    "parentId": 76,
    "comment": "checkin TaskOnBranch test",
    "creationDate": "2015-06-02T08:59:12",
    "guid": "799e4fd3-1161-4f8f-be4f-067dbe98ed89",
    "branch": {
      "name": "/main/scm003",
      "id": 1067,
      "parentId": 3,
      "lastChangeset": 1383,
      "comment": "text\r\n",
      "creationDate": "2015-06-02T08:46:11",
      "guid": "e99e8843-9bd9-43eb-ad67-c170b516a032",
      "owner": {
        "name": "testing01",
        "isGroup": false
      },
      "repository": {
        "name": "default",
        "repId": {
          "id": 1,
          "moduleId": 0
        },
        "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
        "owner": {
          "name": "all",
          "isGroup": false
        },
        "server": "localhost:8084"
      }
    },
    "owner": {
      "name": "testing01",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  }
]

Get a single changeset

GET /api/v1/repos/:repname/changesets/:changesetid

Response

Status: 200 OK

{
  "id": 77,
  "parentId": 76,
  "comment": "checkin TaskOnBranch test",
  "creationDate": "2015-06-02T08:59:12",
  "guid": "799e4fd3-1161-4f8f-be4f-067dbe98ed89",
  "branch": {
    "name": "/main/scm003",
    "id": 1067,
    "parentId": 3,
    "lastChangeset": 1383,
    "comment": "text\r\n",
    "creationDate": "2015-06-02T08:46:11",
    "guid": "e99e8843-9bd9-43eb-ad67-c170b516a032",
    "owner": {
      "name": "testing01",
      "isGroup": false
    },
    "repository": {
      "name": "default",
      "repId": {
        "id": 1,
        "moduleId": 0
      },
      "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
      "owner": {
        "name": "all",
        "isGroup": false
      },
      "server": "localhost:8084"
    }
  },
  "owner": {
    "name": "testing01",
    "isGroup": false
  },
  "repository": {
    "name": "default",
    "repId": {
      "id": 1,
      "moduleId": 0
    },
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner": {
      "name": "all",
      "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Changes

Get pending changes in workspace

GET /api/v1/wkspaces/:wkname/changes

Parameters

Name Type Description
types string A comma-separated list detailing the desired change types to display in the response. Available types: added, checkout, changed, copied, replaced, deleted, localdeleted, moved, localmoved, private, ignored, hiddenchanged, controlledchanged, all

Response

Status: 200 OK

[
    {
        "changes": [
            "CH",
            "MV"
        ],
        "path": "c:\\Users\\scm-user\\wkspaces\\main-wk\\audio-prj\\orchestrated.fspro",
        "oldPath": "c:\\Users\\scm-user\\wkspaces\\main-wk\\audio-prj\\audio-prj.fspro",
        "serverPath": "/audio-prj/orchestrated.fspro",
        "oldServerPath": "/audio-prj/audio-prj.fspro",
        "isXlink": false,
        "localInfo": {
            "modifiedTime": "2015-07-02T11:16:07.0042908Z",
            "size": 57906,
            "isMissing": false
        },
        "revisionInfo": {
            "id": 65,
            "parentId": -1,
            "itemId": 187,
            "type": "text",
            "size": 57896,
            "hash": "tLq1aWZ24MGupAHKZAgYFA==",
            "branchId": 3,
            "changesetId": 3,
            "isCheckedOut": false,
            "creationDate": "2015-04-09T09:51:20",
            "repositoryId": {
                "id": 1,
                "moduleId": 0
            },
            "owner": {
                "name": "scm-user",
                "isGroup": false
            }
        }
    }
]

Undo pending changes in workspace

DELETE /api/v1/wkspaces/:wkname/changes

Parameters

Name Type Description
paths string[] A list of file paths to be restored. They can be either full paths or workspace-relative paths. See examples for further info.

Examples

{
    "paths": [
        "c:\\Users\\scm-user\\wkspaces\\main-wk\\audio-prj\\orchestrated.fspro"
    ]
}
{
    "paths": [
        "audio-prj/orchestrated.fspro"
    ]
}

Response

Status: 200 OK

{
    "affectedPaths": [
        "c:\\Users\\scm-user\\wkspaces\\main-wk\\audio-prj\\orchestrated.fspro"
    ]
}

Workspace Update and Switch

Retrieve update/switch status

GET /api/v1/wkspaces/:wkname/update

GET /api/v1/wkspaces/:wkname/switch

Response

Status: 200 OK

{
    "status": "Not running"
}

Status: 200 OK

{
    "status": "Failed",
    "message": "No route to host 'localhost:8084'"
}

Status: 200 OK

{
    "status": "Calculating",
    "totalFiles": 1356,
    "totalBytes": 3246739,
    "updatedFiles": 0,
    "updatedBytes": 0
}

Status: 200 OK

{
    "status": "Running",
    "totalFiles": 1356,
    "totalBytes": 3246739,
    "updatedFiles": 356,
    "updatedBytes": 894433
}

Status: 200 OK

{
    "status": "Finished",
    "totalFiles": 1356,
    "totalBytes": 3246739,
    "updatedFiles": 1356,
    "updatedBytes": 3246739
}

Launch a workspace update

POST /api/v1/wkspaces/:wkname/update

Response

Status: 200 OK

{
    "status": "Running",
    "totalFiles": 0,
    "totalBytes": 0,
    "updatedFiles": 0,
    "updatedBytes": 0
}

Switch workspace to a different branch/label/changeset

POST /api/v1/wkspaces/:wkname/switch

Parameters

Name Type Description
objectType string Required. The type of switch destination. It can be changeset, label or branch. See examples for further detail.
object string Required. The target point the workspace will be set to.

Examples

{
    "objectType": "branch",
    "object": "/main/task001"
}
{
    "objectType": "changeset",
    "object": "1136"
}
{
    "objectType": "label",
    "object": "BL001"
}

Response

Status: 200 OK

{
    "status": "Running",
    "totalFiles": 0,
    "totalBytes": 0,
    "updatedFiles": 0,
    "updatedBytes": 0
}

Checkin

Retrieve checkin status

GET /api/v1/wkspaces/:wkname/checkin

Response

Status: 200 OK

{
    "status": "Not running"
}

Status: 200 OK

{
    "status": "Failed",
    "message": "No route to host 'localhost:8084'"
}

Status: 200 OK

{
    "status": "Checkin finished",
    "totalSize": 57990,
    "transferredSize": 57990
}

Check in pending changes

POST /api/v1/wkspaces/:wkname/checkin

Parameters

Name Type Description
paths string[] The list of target paths to be checked in. Set to the workspace root if empty or not present.
comment string The checkin comment.
recurse boolean If set to true, directories present in the paths parameter will have their children recursively checked in. If paths is empty or not present, its value is overridden to true. Defaults to false.

Examples

{
}
{
    "comment": "Upgrade core engine"
}
{
    "paths": [
      "src/foo.c",
      "src/bar/baz.c",
      "doc"
    ],
    "comment": "Upgrade core engine",
    "recurse": true
}

Response

Status: 200 OK

{
    "status": "Checkin operation starting...",
    "totalSize": 0,
    "transferredSize": 0
}

Repository contents

Retrieve an item

GET /api/v1/repos/:repname/branches/:branchname/contents/:itempath

GET /api/v1/repos/:repname/changesets/:changesetid/contents/:itempath

GET /api/v1/repos/:repname/labels/:labelname/contents/:itempath

Directories will contain a list of items. Xlinks will contain an object referencing to the xlinked changeset.

Examples

GET /api/v1/repos/myrepo/branches/main/scm003/contents/src/lib/foo.c

GET /api/v1/repos/myrepo/changesets/5378/contents/src/lib/foo.c

GET /api/v1/repos/myrepo/labels/BL001/contents/src/lib/foo.c

Response

Status: 200 OK

{
  "revisionId": 771,
  "type": "file",
  "size": 57913,
  "name": "soundproject.fspro",
  "path": "/fmod/soundproject.fspro",
  "isUnderXlink": false,
  "hash": "/2ygGGfoXDq9bbKZJCzj9g==",
  "content": "http://localhost:9090/api/v1/repos/myrepo/revisions/771/blob",
  "repository": {
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "myrepo",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Status: 200 OK

{
  "revisionId": 3648,
  "type": "directory",
  "size": 0,
  "name": "lib",
  "path": "/lib",
  "isUnderXlink": false,
  "items": [
    {
      "revisionId": 6878,
      "type": "xlink",
      "size": 0,
      "name": "xlink",
      "path": "/lib/xlink",
      "xlinkTarget": {
        "changesetGuid": "41acf2bd-1484-4679-a634-2570d9a7801b",
        "changesetId": 2,
        "repository": "big",
        "server": "localhost:8084"
      }
    }
  ],
  "repository": {
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "myrepo",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Status: 200 OK

{
  "revisionId": 2427,
  "type": "directory",
  "size": 0,
  "name": "mono",
  "path": "/lib/xlink/mono",
  "isUnderXlink": true,
  "items": [
    {
      "revisionId": 46348,
      "type": "file",
      "size": 26383,
      "name": "COPYING.LIB",
      "path": "/lib/xlink/mono/COPYING.LIB",
      "hash": "uFbSL8ON1UNln1XUYeAc7w==",
      "content": "http://localhost:9090/api/v1/repos/myrepo/revisions/46348/blob"
    },
    {
      "type": "directory",
      "size": 0,
      "name": "data",
      "path": "/lib/xlink/mono/data"
    }
  ],
  "repository": {
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "myrepo",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Retrieve a single revision

GET /api/v1/repos/:repname/revisions/:revid

Response

Status: 200 OK

{
  "revisionId": 3526,
  "type": "file",
  "size": 71,
  "name": "foo.c",
  "path": "/src/lib/foo.c",
  "changeset": 1406,
  "branch": "br:/main/scm003",
  "hash": "tNYiUA4VyMJxJrK0kic7mg==",
  "contents": "http://localhost:9090/api/v1/repos/myrepo/revisions/3526/blob",
  "repository": {
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "myrepo",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Status: 200 OK

{
  "revisionId": 5355,
  "type": "directory",
  "size": 0,
  "name": "lib",
  "path": "/src/lib",
  "changeset": 1406,
  "branch": "br:/main/scm003",
  "repository": {
    "repId":
    {
        "id": 1,
        "moduleId": 0
    },
    "name": "myrepo",
    "guid": "c43e1cf9-50b0-4e0d-aca5-c1814d016425",
    "owner":
    {
        "name": "all",
        "isGroup": false
    },
    "server": "localhost:8084"
  }
}

Retrieve revision contents

GET /api/v1/repos/:repname/revisions/:revid/blob

Response

Stream: application/octet-stream

Retrieve the revision history of an item

GET /api/v1/repos/:repname/branches/:branchname/history/:itempath

GET /api/v1/repos/:repname/changesets/:changesetid/history/:itempath

GET /api/v1/repos/:repname/labels/:labelname/history/:itempath

Examples

GET /api/v1/repos/myrepo/branches/main/scm003/history/src/lib/foo.c

GET /api/v1/repos/myrepo/changesets/5378/history/src/lib/foo.c

GET /api/v1/repos/myrepo/labels/BL001/history/src/lib/foo.c

Response

Status: 200 OK

[
    {
        "revisionId": 104,
        "type": "text",
        "owner": {
            "name": "tester",
            "isGroup": false
        },
        "creationDate": "2015-04-09T09:51:20",
        "comment": "Restore method implementation",
        "revisionLink": "http://localhost:9090/api/v1/repos/myrepo/revisions/104",
        "changesetId": 3,
        "changesetLink": "http://localhost:9090/api/v1/repos/myrepo/changesets/3",
        "branchName": "/main",
        "branchLink": "http://localhost:9090/api/v1/repos/myrepo/branches/main",
        "repositoryName": "myrepo",
        "repositoryLink": "http://localhost:9090/api/v1/repos/myrepo"
    }
]

Diff

Changeset diff

GET /api/v1/repos/:repname/changesets/:changesetid/diff

GET /api/v1/repos/:repname/changesets/:changesetid/diff/:sourcechangesetid

If no source changeset ID is passed, the diff operation is performed using the parent changeset as source.

Response

Status: 200 OK

[
  {
    "status": "Changed",
    "path": "/lib/xlink",
    "isDirectory": true,
    "isUnderXlink": false,
    "xlink": {
      "changesetGuid": "ff92e897-b662-40f1-9a1f-a17349cbc7c6",
      "repository": "big",
      "server": "localhost:8084"
    },
    "baseXlink": {
      "changesetGuid": "c75c04ec-3546-46e4-bbb7-031b523dca7d",
      "repository": "big",
      "server": "localhost:8084"
    },
    "isItemFSProtectionChanged": false,
    "itemFileSystemProtection": "NOT_DEFINED",
    "repository": {
      "name": "default",
      "server": "localhost:8084"
    }
  },
  {
    "status": "Changed",
    "path": "/lib/xlink/mono/.gitignore",
    "revisionId": 150892,
    "isDirectory": false,
    "size": 1616,
    "hash": "u0gJQzQnjLNUUHRI1+QQLg==",
    "isUnderXlink": true,
    "merges": [
      {
        "mergeType": "Merged",
        "sourceChangeset": {
          "id": 3,
          "parentId": 2,
          "comment": "multiple changes",
          "creationDate": "2015-07-16T10:01:32",
          "guid": "4d064ea0-4694-41b2-adec-15c3df243dd7",
          "branch": {
            "name": "\/main\/scm002",
            "id": 150865,
            "parentId": 3,
            "lastChangeset": 3,
            "comment": "",
            "creationDate": "2015-07-16T10:01:30",
            "guid": "764c4842-aab8-451a-b802-29162d2a399f",
            "owner": {
              "name": "tester",
              "isGroup": false
            },
            "repository": {
              "name": "big",
              "repId": {
                "id": 4,
                "moduleId": 0
              },
              "guid": "301b269c-6d24-4347-afc1-f095da43f3f2",
              "owner": {
                "name": "testing01",
                "isGroup": false
              },
              "server": "localhost:8084"
            }
          },
          "repository": {
            "name": "big",
            "repId": {
              "id": 4,
              "moduleId": 0
            },
            "guid": "301b269c-6d24-4347-afc1-f095da43f3f2",
            "owner": {
              "name": "testing01",
              "isGroup": false
            },
            "server": "localhost:8084"
          }
        }
      }
    ],
    "isItemFSProtectionChanged": false,
    "itemFileSystemProtection": "NOT_DEFINED",
    "modifiedTime": "2015-07-16T10:04:21",
    "createdBy": {
      "name": "tester",
      "isGroup": false
    },
    "repository": {
      "name": "big",
      "repId": {
        "id": 4,
        "moduleId": 0
      },
      "server": "localhost:8084"
    }
  }
]

Branch diff

GET /api/v1/repos/:repname/branches/:branchname/diff

Response

Status: 200 OK

[
  {
    "status": "Changed",
    "path": "/lib/xlink",
    "isDirectory": true,
    "isUnderXlink": false,
    "xlink": {
      "changesetGuid": "ff92e897-b662-40f1-9a1f-a17349cbc7c6",
      "repository": "big",
      "server": "localhost:8084"
    },
    "baseXlink": {
      "changesetGuid": "c75c04ec-3546-46e4-bbb7-031b523dca7d",
      "repository": "big",
      "server": "localhost:8084"
    },
    "isItemFSProtectionChanged": false,
    "itemFileSystemProtection": "NOT_DEFINED",
    "repository": {
      "name": "default",
      "server": "localhost:8084"
    }
  },
  {
    "status": "Changed",
    "path": "/lib/xlink/mono/.gitignore",
    "revisionId": 150892,
    "isDirectory": false,
    "size": 1616,
    "hash": "u0gJQzQnjLNUUHRI1+QQLg==",
    "isUnderXlink": true,
    "merges": [
      {
        "mergeType": "Merged",
        "sourceChangeset": {
          "id": 3,
          "parentId": 2,
          "comment": "multiple changes",
          "creationDate": "2015-07-16T10:01:32",
          "guid": "4d064ea0-4694-41b2-adec-15c3df243dd7",
          "branch": {
            "name": "\/main\/scm002",
            "id": 150865,
            "parentId": 3,
            "lastChangeset": 3,
            "comment": "",
            "creationDate": "2015-07-16T10:01:30",
            "guid": "764c4842-aab8-451a-b802-29162d2a399f",
            "owner": {
              "name": "tester",
              "isGroup": false
            },
            "repository": {
              "name": "big",
              "repId": {
                "id": 4,
                "moduleId": 0
              },
              "guid": "301b269c-6d24-4347-afc1-f095da43f3f2",
              "owner": {
                "name": "testing01",
                "isGroup": false
              },
              "server": "localhost:8084"
            }
          },
          "repository": {
            "name": "big",
            "repId": {
              "id": 4,
              "moduleId": 0
            },
            "guid": "301b269c-6d24-4347-afc1-f095da43f3f2",
            "owner": {
              "name": "testing01",
              "isGroup": false
            },
            "server": "localhost:8084"
          }
        }
      }
    ],
    "isItemFSProtectionChanged": false,
    "itemFileSystemProtection": "NOT_DEFINED",
    "modifiedTime": "2015-07-16T10:04:21",
    "createdBy": {
      "name": "tester",
      "isGroup": false
    },
    "repository": {
      "name": "big",
      "repId": {
        "id": 4,
        "moduleId": 0
      },
      "server": "localhost:8084"
    }
  }
]

Workspace actions

Add

POST api/v1/wkpaces/:wkname/content/:path

Parameters

Name Type Description
addPrivateParents boolean If true, the command will add any parent directories which are not under version control yet.
checkoutParent boolean If true, the parent node of the selected item will be checked out as a result.
recurse boolean If true, causes all the children items to be recursively added.

Example

POST http://localhost:9090/api/v1/wkspaces/mywk/content/src/lib

{
  "addPrivateParents": true,
  "checkoutParent": false,
  "recurse": true
}

Response

Status: 200 OK

{
    "affectedPaths": [
      "c:\\wkspaces\\mywk\\src\\lib\\descriptor.h",
      "c:\\wkspaces\\mywk\\src\\lib\\code.c"
    ]
}

Checkout

PUT api/v1/wkspaces/:wkname/content/:path

Example

PUT http://localhost:9090/api/v1/wkspaces/mywk/content/src/lib/code.c

Response

Status: 200 OK

{
    "affectedPaths": [
      "c:\\wkspaces\\mywk\\src\\lib\\code.c"
    ]
}

Move

PATCH api/v1/wkspaces/:wkname/content/:path

Parameters

Name Type Description
destination string Required. Destination path to move the selected item.

Example

PATCH http://localhost:9090/api/v1/wkspaces/mywk/content/src/lib/foo.c

{
  "destination": "src/bar.c"
}

Response

Status: 200 OK

{
    "affectedPaths": [
      "c:\\wkspaces\\mywk\\src\\bar.c"
    ]
}

Current limitations

As you know, the Plastic SCM client REST API is still in a development stage. This means that not every CLI/GUI operation is available for our API at the moment, much to our regret.


Unavailable operations

  • Merge from/to
  • Query pagination
  • Xlink management
  • Replication
  • Permissions management
  • Shelves
  • Revert
  • Undelete
  • File info
  • Code reviews
  • Annotate code
  • Attribute management
  • Statistics
  • Trigger management
  • Gluon compatibility (cm partial)

Last updated

February 26, 2018
  • We added the configuration steps and examples to starting the local API.
  • August 4, 2016
  • Fixed the parameter names in the Switch workspace to a different branch/label/changeset reference.
  • July 7, 2016
  • Limitation removed - The execution on Linux is available.
  • August 31, 2015
  • Welcome to the Plastic SCM client REST API documentation.