mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
rest as a database backend
This commit is contained in:
parent
634fa80d10
commit
d8551b9909
28
README.md
28
README.md
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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'));
|
||||
});
|
||||
|
||||
// })
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user