Skip to content

Commit

Permalink
Add API to fetch repository status via uhub (#21)
Browse files Browse the repository at this point in the history
* Add API to fetch repository status via uhub

* Dont run localFile tests for github

* Document test scripts

* Rename into LocalUtils, move test into ./api

* Fix LocalFile properties getters

* Implement LocalUtils.track()

* Update comments based on coding review

* Remove unused FILETYPE variable

* Add status and expected behaviour to comment
  • Loading branch information
mklabs authored and Soreine committed Nov 15, 2016
1 parent 49dfd96 commit b062e16
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 23 deletions.
53 changes: 32 additions & 21 deletions README.md
Expand Up @@ -9,13 +9,13 @@ It allows more complex operations than the [Contents API](https://developer.gith

It is powered by an immutable model. Async operations are Promise-based.

### Installation
## Installation

```
$ npm install repofs
```

### How to use it?
## How to use it?

To use `repofs` in the browser, include it using browserify/webpack.

Expand All @@ -33,15 +33,15 @@ var driver = repofs.GitHubDriver({
});
```

#### Start with an empty RepositoryState
### Start with an empty RepositoryState

The first step is to create an instance of `RepositoryState`:

```js
var repoState = repofs.RepositoryState.createEmpty();
```

#### Fetch the list of branches
### Fetch the list of branches

After creating a `RepositoryState`, the next step is to fetch the list of existing branches.

Expand All @@ -53,7 +53,7 @@ repofs.RepoUtils.fetchBranches(repoState, driver)
})
```

#### Checkout a branch
### Checkout a branch

Once the branches are fetched, you can checkout one. **This requires to fetch it first** using `repofs.RepoUtils.fetchTree`. This overrides any existing working tree for this branch. The `repofs.RepoUtils.checkout` operation is always sync.

Expand All @@ -67,7 +67,7 @@ repofs.RepoUtils.fetchTree(repoState, driver, branch)
})
```

#### Quick initialization
### Quick initialization

There is a short way to initialize a `RepositoryState` from a driver, that will fetch the list of the branches, then fetch and checkout *master* or the first available branch.

Expand All @@ -79,7 +79,7 @@ repofs.RepoUtils.initialize(driver)
});
```

#### Reading files
### Reading files

Reading a file requires to fetch the content from the remote repository inside the `RepositoryState` (See [Caching](#caching)):

Expand All @@ -100,7 +100,7 @@ var blob = repofs.FileUtils.read(repoState, 'README.md');
var content = repofs.FileUtils.readAsString(repoState, 'README.md');
```

#### Listing files
### Listing files

repofs keeps the whole trees in the different `WorkingStates`, you can access the whole tree as a flat list...

Expand All @@ -116,7 +116,7 @@ var dir = '.' // root
var rootTree = repofs.TreeUtils.get(repoState, dir);
```

#### Working with files
### Working with files

Create a new file:

Expand All @@ -142,7 +142,7 @@ Rename/Move the file
var newRepoState = repofs.FileUtils.move(repoState, 'API.md', 'API2.md');
```

#### Working with directories
### Working with directories

List files in the directory

Expand All @@ -162,7 +162,7 @@ Rename/Move the directory
var newRepoState = repofs.DirUtils.move(repoState, 'myfolder', 'myfolder2');
```

#### Changes
### Changes

Until being commited, repofs keeps a record of changes per files.

Expand All @@ -182,7 +182,7 @@ var newRepoState = repofs.ChangeUtils.revertForFile(repoState, 'README.md');
var newRepoState = repofs.ChangeUtils.revertForDir(repoState, 'src');
```

#### Commiting changes
### Commiting changes

```js
// Create an author / committer
Expand All @@ -202,7 +202,7 @@ repofs.CommitUtils.flush(repoState, driver, commitBuilder)
});
```

#### Manipulating branches
### Manipulating branches

``` js
// Create a branch from current branch
Expand All @@ -220,7 +220,7 @@ repofs.CommitUtils.flush(repoState, driver, commitBuilder)
});
```

#### Non fast forward commits
### Non fast forward commits

Flushing a commit can fail with an `ERRORS.NOT_FAST_FORWARD` code.

Expand Down Expand Up @@ -258,7 +258,7 @@ Non fast forward errors contains the created commit (that is currently not linke
}
```

#### Merging
### Merging

`repofs.BranchUtils.merge` allows to automatically merge a commit or a branch, into another branch.

Expand All @@ -270,7 +270,7 @@ repofs.BranchUtils.merge(repoState, driver, from, into)
});
```

#### Merge conflicts
### Merge conflicts

But conflicts can happen when the automatic merge failed. For example, after merging two branches, or after merging a non fast forward commit. It is possible then to solve the conflicts manually:

Expand Down Expand Up @@ -315,11 +315,11 @@ function solveConflicts(repoState, driver, from, into) {
}
```

#### Remotes operations
### Remotes operations

When using a compatible API, you can also deal with remotes on the repository.

##### List remotes
#### List remotes

``` js
repofs.RemoteUtils.list(driver)
Expand All @@ -332,7 +332,7 @@ repofs.RemoteUtils.list(driver)
});
```

##### Edit remotes
#### Edit remotes

``` js
repofs.RemoteUtils.edit(driver, name, url)
Expand All @@ -341,7 +341,7 @@ repofs.RemoteUtils.edit(driver, name, url)
});
```

##### Pulling
#### Pulling

You can update a branch to the state of the same branch on a remote, and get an updated `RepositoryState` with:

Expand All @@ -364,7 +364,7 @@ repofs.RemoteUtils.pull(repoState, driver, {
})
```

##### Pushing
#### Pushing

You can push a branch to a remote:

Expand All @@ -386,3 +386,14 @@ repofs.RemoteUtils.push(repoState, driver, {
// Pushed
})
```

## Contributing

### Run tests

You can run all the tests by providing a `GITHUB_TOKEN` with permission to create and write to a GitHub repository, and running `npm run test`.

You can run tests with GitHub as a backend `npm run test-github`, or Uhub as a backend `npm run test-uhub`.

Finally, you can run the tests without testing the API through the drivers by running `npm run test-no-api`.

55 changes: 55 additions & 0 deletions lib/drivers/driver.js
Expand Up @@ -156,4 +156,59 @@ Driver.prototype.pull = function (opts) {};
*/
Driver.prototype.push = function (opts) {};

/**
* Fetch status information from a remote repository
* @param {Branch} opts.branch Branch to push. Default to current
* @return {Promise<LocalFile>}
*/
Driver.prototype.status = function (opts) {};

/**
* Track local files into a git repository. Performs a post request to /track
* endpoint with a JSON payload in the form of:
*
*
* {
* "message": "Commit message",
* "files": [{
* "name": "README.md",
* "status": "modified"
* }, {
* "name": "foobar.md",
* "status": "untracked"
* }],
*
* "author": {
* "name": "John Doe",
* "email": "johndoe@example.com",
* "date": "Mon Nov 07 2016 16:40:35 GMT+0100 (CET)"
* },
*
* "committer": {
* "name": "John Doe",
* "email": "johndoe@example.com",
* "date": "Mon Nov 07 2016 16:40:35 GMT+0100 (CET)"
* }
* }
*
* Status and expected behaviour:
*
* added -> File will be added and committed
* copied -> not supported
* removed -> File will be removed
* modified -> Modification will be committed
* renamed -> not supported
* unmodified -> Nothing
* untracked -> File will be added and committed
*
* @param {String} opts.message commit message.
* @param {Array<LocalFile>} opts.files List of files with name and status.
* @param {Author} opts.author Optional author information with name, email and
* date.
* @param {Author} opts.committer Optional committer information with name,
* email and date.
* @return {Promise<Undefined>}
*/
Driver.prototype.track = function (opts) {};

module.exports = Driver;
41 changes: 41 additions & 0 deletions lib/drivers/github.js
Expand Up @@ -16,6 +16,7 @@ var Commit = require('../models/commit');
var Author = require('../models/author');
var TreeEntry = require('../models/treeEntry');
var WorkingState = require('../models/workingState');
var LocalFile = require('../models/localFile');

/**
* Options for the GitHub Driver
Expand Down Expand Up @@ -297,6 +298,46 @@ GitHubDriver.prototype.push = function (opts) {
.fail(normUnknownRemote);
};

GitHubDriver.prototype.status = function (opts) {
var branch = opts.branch.getName();
return this.get('status/' + branch)
.then(function(status) {
return new Immutable.List(status.files).map(function(file) {
return LocalFile.create(file);
});
});
};

GitHubDriver.prototype.track = function (opts) {
var params = {
message: opts.message,
files: opts.files.map(function (file) {
return {
name: file.getFilename(),
status: file.getStatus()
};
}).toJS()
};

if (opts.author) {
params.author = {
name: opts.author.getName(),
email: opts.author.getEmail(),
date: opts.author.getDate()
};
}

if (opts.committer) {
params.committer = {
name: opts.author.getName(),
email: opts.author.getEmail(),
date: opts.author.getDate()
};
}

return this.post('track', params);
};


// API utilities

Expand Down
2 changes: 2 additions & 0 deletions lib/index.js
Expand Up @@ -17,6 +17,7 @@ var ERRORS = require('./constants/errors');
var WorkingUtils = require('./utils/working');
var TreeUtils = require('./utils/filestree');
var FileUtils = require('./utils/file');
var LocalUtils = require('./utils/localFile');
var BlobUtils = require('./utils/blob');
var DirUtils = require('./utils/directory');
var RepoUtils = require('./utils/repo');
Expand Down Expand Up @@ -49,6 +50,7 @@ module.exports = {
WorkingUtils: WorkingUtils,
TreeUtils: TreeUtils,
FileUtils: FileUtils,
LocalUtils: LocalUtils,
BlobUtils: BlobUtils,
DirUtils: DirUtils,
RepoUtils: RepoUtils,
Expand Down
66 changes: 66 additions & 0 deletions lib/models/localFile.js
@@ -0,0 +1,66 @@
var Immutable = require('immutable');
var path = require('path');
var mime = require('mime-types');

var FILETYPE = require('../constants/filetype');

var LocalFile = Immutable.Record({
// Sha1 of the modified blob,
sha: null,

// Path of the file
filename: '',

// File status
status: '',

// Number of additions
additions: 0,

// Number of deletions
deletions: 0,

// Number of changes
changes: 0,

// Git patch to apply
patch: ''
}, 'LocalFile');

// ---- Properties Getter ----
LocalFile.prototype.getSha = function() {
return this.get('sha');
};

LocalFile.prototype.getFilename = function() {
return this.get('filename');
};

LocalFile.prototype.getStatus = function() {
return this.get('status');
};

LocalFile.prototype.getAdditions = function() {
return this.get('additions');
};

LocalFile.prototype.getDeletions = function() {
return this.get('deletions');
};

LocalFile.prototype.getChanges = function() {
return this.get('changes');
};

LocalFile.prototype.getPatch = function() {
return this.get('patch');
};

/**
* Create a LocalFile representing a status result at the given path (filename etc.)
*/
LocalFile.create = function (file) {
return new LocalFile(file);
};

module.exports = LocalFile;

0 comments on commit b062e16

Please sign in to comment.