0 Design: Projects
Nick O'Leary edited this page 2017-10-25 22:02:44 +01:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This is a work in progress - I'm trying to capture the design as it evolves.

Last Update: 25th October.

Working code in the projects branch.

Description

  • A project is a set of files that represent a complete, redistributable Node-RED application.
  • The Runtime runs a single project at any one time, but the Editor provides an easy way to switch between projects.
  • A project exists locally on disk, but can be linked to a remote git repository

Project files

Projects exist in the directory ~/.node-red/projects/

.node-red/
├── node_modules
├── package.json
├── projects
│   └── my-project
│       ├── .git
|       ├── .gitignore 
│       ├── flow.json
│       ├── flow_cred.json
│       ├── package.json
│       ├── README.md
│       └── settings.json
└── settings.js
  • A default project consists of:

    • flow.json / flow_cred.json the flow & credential files
    • package.json lists any node module dependencies
    • README.md documentation for the flow
    • settings.json project specific settings
  • The name of the flow file (default flow.json) will be customisable

  • The editor will provide facilities to edit each of these files in the appropriate context.

  • TBD: A project could contain other files (such as public/html content). Should NR allows you to edit/manage them as well? We dont want to become a generic IDE.

package.json

  • The package.json file is the standard npm format.
  • It lists the npm dependencies and any other project metadata.
  • The dependencies do not get installed in the project directory they get installed at the top level of the user directory (as they would today).
  • It includes a node-red section, under which there is a settings section

settings.json

25-Oct: The purpose of the settings.json file is not fully defined yet.

  • The settings.json file contains project specific settings. They are a subset of those available in the top-level settings.js file. TBD: exactly what settings are supported.
  • The settings file is strictly JSON. It cannot contain code as it must be reloadable without side-effects and be writeable by the runtime.
  • Introduces the concept of flow-variables:
    • Extend the existing $(ENV_VAR) support to include properties defined in the settings file.
    • This makes it easier to deploy a flow to two different environments with environment-specific flow properties.
    • TBD: describe the workflow for that.

Design

  • Runtime use of Projects
  • Feature Flag
  • Storage API
  • Admin API

Runtime use of Projects

The goal is to minimise the changes needed in the core of the runtime. There is no need for the core runtime to be aware of projects - it just runs the flows it is given.

The current model is the runtime loads its flows/credentials from the Storage API (getFlows) and we already provide an API to tell the runtime to reload its flows from Storage. None of that needs to change. The concept of projects will exist within the Storage layer.

getFlows/Credentials will return the current active project flows. When the active project changes, the runtime is told to reload its flows and the storage api will return the new project's flows.

Feature Flag

The projects feature will be behind a feature flag. The flag will be on by default but automatically disabled if:

  • the user's settings file explicitly disabled projects (for example, in hosted environments)
  • the storage plugin does not support the new projects api

Storage API

The storage api will be extended to include the basic projects api. Not all storage implementations will be required to implement the project apis.

StorageModule.projects = {
init: init,
    listProjects: Projects.list,
    getActiveProject: getActiveProject,
    setActiveProject: setActiveProject,
    getProject: getProject,
    createProject: createProject,
    updateProject: updateProject,
    getFiles: getFiles,
    getFile: getFile,
    stageFile: stageFile,
    unstageFile: unstageFile,
    commit: commit,
    getFileDiff: getFileDiff,
    getCommits: getCommits,
    getCommit: getCommit,
}

Note: this api will grow as the feature is developed - this isn't the final API.

...

Admin API

This API is subject to change as the implementation evolves.

  • GET / - List projects
  • POST / - Create project
  • PUT /:id - Update project
  • GET /:id - Get project
  • DELETE /:id - Delete project (TODO)
  • GET /:id/files - Get a projects file listing
  • GET /:id/files/:treeish/:filePath - Get a file for a given treeish description,
  • POST /:id/stage/:filePath - Stage a file from the working dir to index
  • POST /:id/stage - Stage multiple files from the working dir to index
  • POST /:id/commit - Commit the current index
  • DELETE /:id/stage/:filePath - Unstage a file from the index
  • DELETE /:id/stage - Unstage multiple files from the index
  • GET /:id/diff/:type/:filePath - Get a file diff (type = tree | index)
  • GET /:id/commits - Get the project commits
  • GET /:id/commits/:sha - Get an individual commit

GET /projects

Returns a list of all projects available in the editor.

{
   "active": "the name of the active project",
   "projects": [
        "project-a",
        "another-project"
   ]
}

Notes: this may change to provide a bit more meta-data about each project rather than a flat list.

POST /projects

Create a new project.

{
    "name": "my-new-project",
    "remote": {
        "url": "https://my-git-url.git"
    }
    "credentialSecret": "my-secret-key"
}

Notes:

  • if remote isn't specified, this is a local-only project
  • if remote is specified and auth is required... need to design the flows

GET /projects/:id

Get a project's metadata

{
    "name": "my-project",
    "description": "my-project\n====\n\nA Node-RED Project\n\n",
    "dependencies": {
        "node-red-node-random": "0.0.13"
    }
}

Possible error states for a project

If a project is in some sort of error state, its meta data will include an errors property:

{
   "name": "my-project",
   ...
   "errors": [
      {"error":"error_code","message":"error_description"}
   ]
}

Possible values for error_code are:

  • invalid_credential_secret - the credentials are encrypted and we don't have the key
  • missing_dependencies - modules listed in the project's package.json file are not installed
  • missing_flow_file - the cloned project does not appear to contain a flow file

PUT /projects/:id

Update an existing project.

Available actions include:

  • sets the active project
  • update the credentialSecret
  • update remote repository details, including providing credentials when needed.

Note: need to refine how credentials can be challenged for and provided.

Set active project

{
   "active": true
}

DELETE /projects/:id

Delete the project

GET /projects/:id/files

Get a list of project files - subject to change with Version Control API design

GET /projects/:id/files/path/to/file

Get an individual file's contents - - subject to change with Version Control API design

POST /projects/:id/files/path/to/file

Update an individual file's contents - - subject to change with Version Control API design

REST Flows

  • Create an empty project
  • Create project with remote repo - no authentication needed
  • Create project with remote repo - authentication needed
  • Create a project as a copy of an existing one
  • Set the active project in the runtime

Create an empty project

client                                                runtime
------                                                -------

{                             POST /projects
  "name": "<name>"         ------------------>         - create project directory
}                                                      - git init
                                                       - create default files
                          REDIRECT GET /projects/<name>
                            <-----------------

Create project with remote repo - no authentication needed

client                                                runtime
------                                                -------

{                             POST /projects
  "name": "<name>"         ------------------>         - create project directory
  "remote": {                                          - git init
    "url": "<giturl>"                                  - git pull remote
  }
}                                                     
                          REDIRECT GET /projects/<name>
                            <-----------------

Create project with remote repo - authentication needed

client                                                runtime
------                                                -------

{                             POST /projects
  "name": "<name>"         ------------------>         - create project directory
  "remote": {                                          - git init
    "url": "<giturl>"                                  - git pull remote
  }                                                    - auth-fail - delete project directory
}                                                     
                   400: { error: "git_auth_fail", message: ""}
                            <-----------------

- gather auth info from user

{                             POST /projects
  "name": "<name>"         ------------------>         - create project directory
  "remote": {                                          - git init
    "url": "<giturl>",                                 - git pull remote inc credentials
    "auth": {
      "username": "nick",
      "password": "pass"
  }
}                                                     
                          REDIRECT GET /projects/<name>
                            <-----------------

Create a project as a copy of an existing one

client                                                runtime
------                                                -------

{                             POST /projects
  "name": "<toName>",      ------------------>         - create project directory
  "copy": "<fromName>"                                 - git init
}                                                      - git pull from existing project
                                                       
                        REDIRECT GET /projects/<toName>
                            <-----------------

Set the active project in the runtime

client                                                runtime
------                                                -------

{                        PUT /projects/<name>
  "active": "true"      ------------------>         - update the active project
}                                                   - tell the runtime to reload its flows
                                                       
                        REDIRECT GET /projects/<name>
                            <-----------------