2
0
mirror of https://github.com/frappe/books.git synced 2024-09-19 19:19:02 +00:00

rest as a database backend

This commit is contained in:
Rushabh Mehta 2018-01-03 15:42:40 +05:30
parent 634fa80d10
commit d8551b9909
6 changed files with 215 additions and 42 deletions

View File

@ -95,7 +95,7 @@ await frappe.init();
await frappe.init_db('sqlite', {db_path: 'test.db'});
// get all open todos
let todos = await frappe.db.get_all('ToDo', ['name'], {status: "Open"});
let todos = await frappe.db.get_all({doctype:'ToDo', fields:['name'], filters: {status: "Open"});
let first_todo = await frappe.get_doc('ToDo', toods[0].name);
```
@ -109,7 +109,7 @@ await frappe.init();
await frappe.init_db('sqlite', {db_path: 'test.db'});
// get all open todos
let todos = await frappe.db.get_all('ToDo', ['name'], {status: "Open"});
let todos = await frappe.db.get_all({doctype:'ToDo', fields:['name'], filters: {status: "Open"});
let first_todo = await frappe.get_doc('ToDo', toods[0].name);
first_todo.status = 'Closed';
@ -126,7 +126,7 @@ await frappe.init();
await frappe.init_db('sqlite', {db_path: 'test.db'});
// get all open todos
let todos = await frappe.db.get_all('ToDo', ['name'], {status: "Open"});
let todos = await frappe.db.get_all({doctype:'ToDo', fields:['name'], filters: {status: "Open"});
let first_todo = await frappe.get_doc('ToDo', toods[0].name);
await first_todo.delete();
@ -281,6 +281,28 @@ Response:
}
]
```
## REST Client
Frappe comes with a built in REST client so you can also use REST as a database backend with the frappe API
### Create, Read, Update, Delete
You can manage documents, using the same Document API as if it were a local database
```js
await frappe.init();
await frappe.init_db('rest', {server: 'localhost:8000'});
let doc = await frappe.get_doc({doctype:'ToDo', subject:'test rest insert 1'});
await doc.insert();
doc.subject = 'subject changed';
await doc.update();
let data = await frappe.db.get_all({doctype:'ToDo'});
```
## Tests
All tests are in the `tests` folder and are run using `mocha`. To run tests

View File

@ -0,0 +1,89 @@
const frappe = require('frappe-core');
const path = require('path');
const fetch = require('node-fetch');
class RESTClient {
constructor({server, protocol='http'}) {
this.server = server;
this.protocol = protocol;
this.json_headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
connect() {
}
async insert(doctype, doc) {
doc.doctype = doctype;
let url = this.protocol + '://' + path.join(this.server, `/api/resource/${frappe.slug(doctype)}`);
let response = await fetch(url, {
method: 'POST',
headers: this.json_headers,
body: JSON.stringify(doc)
});
return await response.json();
}
async get(doctype, name) {
let url = this.protocol + '://' + path.join(this.server, `/api/resource/${frappe.slug(doctype)}/${name}`);
let response = await fetch(url, {
method: 'GET',
headers: this.json_headers
});
return await response.json();
}
async get_all({doctype, fields, filters, start, limit, sort_by, order}) {
let url = this.protocol + '://' + path.join(this.server, `/api/resource/${frappe.slug(doctype)}`);
let response = await fetch(url, {
method: 'GET',
params: {
fields: JSON.stringify(fields),
filters: JSON.stringify(filters),
start: start,
limit: limit,
sort_by: sort_by,
order: order
},
headers: this.json_headers
});
return await response.json();
}
async update(doctype, doc) {
doc.doctype = doctype;
let url = this.protocol + '://' + path.join(this.server, `/api/resource/${frappe.slug(doctype)}/${doc.name}`);
let response = await fetch(url, {
method: 'PUT',
headers: this.json_headers,
body: JSON.stringify(doc)
});
return await response.json();
}
async delete(doctype, name) {
let url = this.protocol + '://' + path.join(this.server, `/api/resource/${frappe.slug(doctype)}/${name}`);
let response = await fetch(url, {
method: 'DELETE',
headers: this.json_headers
});
return await response.json();
}
close() {
}
}
module.exports = {
Database: RESTClient
}

View File

@ -88,11 +88,12 @@ class sqliteDatabase {
return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name);
}
get_all(doctype, fields=['name'], filters, start, limit) {
get_all({doctype, fields=['name'], filters, start, limit, order_by='modified', order='desc'} = {}) {
return new Promise(resolve => {
this.conn.all(`select ${fields.join(", ")}
from ${frappe.slug(doctype)}
${filters ? "where" : ""} ${this.get_filter_conditions(filters)}
${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""}
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`,
(err, rows) => {
resolve(rows);
@ -153,7 +154,12 @@ class sqliteDatabase {
filters = {name: filters};
}
let row = await this.get_all(doctype, [fieldname], filters, 0, 1);
let row = await this.get_all({
doctype:doctype,
fields: [fieldname],
filters: filters,
start: 0,
limit: 1});
return row.length ? row[0][fieldname] : null;
}

View File

@ -4,31 +4,78 @@ module.exports = {
setup(app) {
// get list
app.get('/api/resource/:doctype', async function(request, response) {
let data = await frappe.db.get_all(request.params.doctype, ['name', 'subject'], null,
start = request.params.start || 0, limit = request.params.limit || 20);
return response.json(data);
try {
let fields, filters;
for (key of ['fields', 'filters']) {
if (request.params[key]) {
request.params[key] = JSON.parse(request.params[key]);
}
}
let data = await frappe.db.get_all({
doctype: request.params.doctype,
fields: request.params.fields || ['name', 'subject'],
filters: request.params.filters,
start: request.params.start || 0,
limit: request.params.limit || 20,
order_by: request.params.order_by,
order: request.params.order
});
return response.json(data);
} catch (e) {
throw e;
}
});
// create
app.post('/api/resource/:doctype', async function(request, response) {
data = request.body;
data.doctype = request.params.doctype;
let doc = await frappe.get_doc(data)
await doc.insert();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
try {
data = request.body;
data.doctype = request.params.doctype;
let doc = await frappe.get_doc(data);
await doc.insert();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// update
app.put('/api/resource/:doctype/:name', async function(request, response) {
try {
data = request.body;
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
Object.assign(doc, data);
await doc.update();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// get document
app.get('/api/resource/:doctype/:name', async function(request, response) {
let data = await frappe.get_doc(request.params.doctype, request.params.name).get_valid_dict();
return response.json(data);
try {
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// delete
app.delete('/api/resource/:doctype/:name', async function(request, response) {
let data = await frappe.get_doc(request.params.doctype, request.params.name).delete();
return response.json(data);
try {
let doc = await frappe.get_doc(request.params.doctype, request.params.name)
await doc.delete();
return response.json({});
} catch (e) {
throw e;
}
});
}

View File

@ -13,7 +13,7 @@ describe('Database', () => {
await frappe.insert({doctype:'ToDo', subject: 'testing 3'});
await frappe.insert({doctype:'ToDo', subject: 'testing 2'});
let subjects = await frappe.db.get_all('ToDo', ['name', 'subject'])
let subjects = await frappe.db.get_all({doctype:'ToDo', fields:['name', 'subject']})
subjects = subjects.map(d => d.subject);
assert.ok(subjects.includes('testing 1'));
@ -29,16 +29,16 @@ describe('Database', () => {
await frappe.insert({doctype:'ToDo', subject: 'testing 3', status: 'Open'});
await frappe.insert({doctype:'ToDo', subject: 'testing 2', status: 'Closed'});
subjects = await frappe.db.get_all('ToDo', ['name', 'subject'],
{status: 'Open'})
subjects = await frappe.db.get_all({doctype:'ToDo', fields:['name', 'subject'],
filters:{status: 'Open'}});
subjects = subjects.map(d => d.subject);
assert.ok(subjects.includes('testing 1'));
assert.ok(subjects.includes('testing 3'));
assert.equal(subjects.includes('testing 2'), false);
subjects = await frappe.db.get_all('ToDo', ['name', 'subject'],
{status: 'Closed'})
subjects = await frappe.db.get_all({doctype:'ToDo', fields:['name', 'subject'],
filters:{status: 'Closed'}});
subjects = subjects.map(d => d.subject);
assert.equal(subjects.includes('testing 1'), false);

View File

@ -11,11 +11,13 @@ var test_server;
describe('REST', () => {
before(async function() {
await helpers.init_sqlite();
test_server = spawn('node', ['frappe/tests/test_server.js'], {
stdio: [0, 'pipe', 'pipe' ]
stdio: [process.stdin, process.stdout, process.stderr, 'pipe', 'pipe']
});
await frappe.init();
await frappe.init_db('rest', {server: 'localhost:8000'});
// wait for server to start
await frappe.sleep(1);
});
@ -26,28 +28,35 @@ describe('REST', () => {
});
it('should create a document', async () => {
let res = await fetch('http://localhost:8000/api/resource/todo', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({subject: 'test rest insert 1', description: 'test rest description 1'})
});
let doc = await res.json();
assert.equal(doc.subject, 'test rest insert 1');
assert.equal(doc.description, 'test rest description 1');
let doc = await frappe.get_doc({doctype:'ToDo', subject:'test rest insert 1'});
await doc.insert();
let doc1 = await frappe.get_doc('ToDo', doc.name);
assert.equal(doc.subject, doc1.subject);
assert.equal(doc1.status, 'Open');
});
// it('should create a document with rest backend', async () => {
it('should update a document', async () => {
let doc = await frappe.get_doc({doctype:'ToDo', subject:'test rest insert 1'});
await doc.insert();
// frappe.init_db('rest', { server: 'http://localhost:8000' });
doc.subject = 'subject changed';
await doc.update();
// let doc = await frappe.get_doc({doctype: 'ToDo', subject: 'test rest backend 1'});
// await doc.insert();
let doc1 = await frappe.get_doc('ToDo', doc.name);
assert.equal(doc.subject, doc1.subject);
});
// let doc_reloaded = await frappe.get_doc('ToDo', doc.name);
it('should get multiple documents', async () => {
await frappe.insert({doctype:'ToDo', subject:'all test 1'});
await frappe.insert({doctype:'ToDo', subject:'all test 2'});
let data = await frappe.db.get_all({doctype:'ToDo'});
let subjects = data.map(d => d.subject);
assert.ok(subjects.includes('all test 1'));
assert.ok(subjects.includes('all test 2'));
});
// })
});