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 don’t 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 asettings
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 projectsPOST /
- Create projectPUT /:id
- Update projectGET /:id
- Get projectDELETE /:id
- Delete project (TODO)GET /:id/files
- Get a projects file listingGET /:id/files/:treeish/:filePath
- Get a file for a given treeish description,POST /:id/stage/:filePath
- Stage a file from the working dir to indexPOST /:id/stage
- Stage multiple files from the working dir to indexPOST /:id/commit
- Commit the current indexDELETE /:id/stage/:filePath
- Unstage a file from the indexDELETE /:id/stage
- Unstage multiple files from the indexGET /:id/diff/:type/:filePath
- Get a file diff (type = tree | index)GET /:id/commits
- Get the project commitsGET /: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 keymissing_dependencies
- modules listed in the project's package.json file are not installedmissing_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>
<-----------------