@igea/oac_backend 1.0.74 → 1.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igea/oac_backend",
3
- "version": "1.0.74",
3
+ "version": "1.0.76",
4
4
  "description": "Backend service for the OAC project",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -7,6 +7,8 @@ const Converter = require('../models/converter');
7
7
  const Validator = require('../models/validator');
8
8
  const tmp = require('tmp');
9
9
  const Investigations = require('../models/investigations');
10
+ const EditLocks = require('../models/editLocks');
11
+
10
12
  const {
11
13
  fusekiUrlDataset,
12
14
  fusekiUrl,
@@ -149,7 +151,7 @@ router.post('/form/save', (req, res) => {
149
151
  let updateQuery = Converter.turtle2Sparql(dataset, {processQuad, processRoot});
150
152
  Investigations.save({
151
153
  id, uuid, dataset, format: 'turtle'
152
- }).then( () => {
154
+ }).then( (dbResponse) => {
153
155
  axios.post(fusekiUrlUpdate, updateQuery, {
154
156
  headers: {
155
157
  'Content-Type': 'application/sparql-update',
@@ -158,7 +160,7 @@ router.post('/form/save', (req, res) => {
158
160
  }).then(response => {
159
161
  console.log(response.data);
160
162
  res.status(200).json({
161
- success: true
163
+ success: true, data: dbResponse.data
162
164
  });
163
165
  }).catch(error => {
164
166
  //TODO: rollback investigation save
@@ -223,7 +225,69 @@ router.post('/form/search', (req, res) => {
223
225
  })
224
226
 
225
227
  })
228
+ //-----------------------------------------------------------------
229
+
230
+ router.get('/form/lock/:row_id/:client_uuid', (req, res) => {
231
+ const row_id = parseInt(req.params.row_id)
232
+ const client_uuid = req.params.client_uuid
233
+ EditLocks.lock('investigation', row_id, client_uuid).then((success)=>{
234
+ res.status(200).json({
235
+ success,
236
+ message: success ? null : 'Record is locked by another user'
237
+ });
238
+ }).catch((err)=>{
239
+ res.status(500).json({
240
+ success: false,
241
+ data: null,
242
+ message: `Error locking record: ${row_id}`
243
+ });
244
+ })
245
+ })
246
+
247
+ router.get('/form/lock-keep/:row_id/:client_uuid', (req, res) => {
248
+ const row_id = parseInt(req.params.row_id)
249
+ const client_uuid = req.params.client_uuid
250
+ EditLocks.extendLock('investigation', row_id, client_uuid).then((success)=>{
251
+ res.status(200).json({
252
+ success,
253
+ message: success ? null : 'Lock not owned or expired'
254
+ });
255
+ }).catch((err)=>{
256
+ res.status(500).json({
257
+ success: false,
258
+ data: null,
259
+ message: `Error keeping lock on record: ${row_id}`
260
+ });
261
+ })
262
+ })
263
+
264
+ function _unlock(req, res){
265
+ const row_id = parseInt(req.params.row_id)
266
+ const client_uuid = req.params.client_uuid
267
+ EditLocks.delete('investigation', row_id, client_uuid).then((success)=>{
268
+ res.status(200).json({
269
+ success
270
+ });
271
+ }).catch((err)=>{
272
+ res.status(500).json({
273
+ success: false,
274
+ data: null,
275
+ message: `Error unlocking record ${row_id}: ${err}`
276
+ });
277
+ })
278
+ }
279
+
280
+ router.get('/form/unlock/:row_id/:client_uuid', (req, res) => {
281
+ _unlock(req, res)
282
+ })
283
+
284
+ router.post('/form/unlock/:row_id/:client_uuid', (req, res) => {
285
+ _unlock(req, res)
286
+ })
287
+
288
+
226
289
 
290
+ //-----------------------------------------------------------------
227
291
  router.get('/schema/:type/:what', (req, res) => {
228
292
  console.log(`Requesting SHACL schema in format: ${req.params.type}`);
229
293
  let type = req.params.type || 'config';
@@ -0,0 +1,78 @@
1
+ const {db, schema } = require('./db')
2
+ const table = `${schema}.edit_locks`
3
+ const expiration_seconds = 300 // 5 mjinutes
4
+
5
+
6
+ class EditLocks {
7
+
8
+ static lock(table_name, row_id, client_uuid){
9
+ return new Promise(async (resolve, reject) => {
10
+ try{
11
+ let start_ts = Math.ceil(new Date().getTime()/1000)
12
+ let end_ts = start_ts + expiration_seconds
13
+ let sql = `INSERT INTO ${table}(
14
+ table_name, row_id, client_uuid,
15
+ locked_at_ts, expires_at_ts
16
+ )VALUES(?, ?, ?, ?, ?)
17
+ ON CONFLICT (table_name, row_id)
18
+ DO UPDATE
19
+ SET client_uuid = EXCLUDED.client_uuid,
20
+ locked_at_ts = ?, expires_at_ts = ?
21
+ WHERE ${table}.table_name = ?
22
+ AND ${table}.row_id = ?
23
+ AND ${table}.expires_at_ts < ?
24
+ RETURNING client_uuid
25
+ `
26
+ const result = await db.raw(sql, [
27
+ table_name, row_id, client_uuid, start_ts, end_ts,
28
+ start_ts, end_ts,
29
+ table_name, row_id, start_ts
30
+ ])
31
+ let reserver_uuid = null
32
+ if(result.rows.length){
33
+ reserver_uuid = result.rows[0]["client_uuid"]
34
+ }
35
+ console.log(reserver_uuid)
36
+ resolve(reserver_uuid == client_uuid)
37
+ }catch(e){
38
+ reject(e)
39
+ }
40
+ });
41
+ }
42
+
43
+ static extendLock(table_name, row_id, client_uuid){
44
+ return new Promise(async (resolve, reject) => {
45
+ try{
46
+ const cur_ts = Math.ceil((new Date()).getTime()/1000)
47
+ const updated = await db(table).
48
+ where({ table_name, row_id, client_uuid })
49
+ .andWhere('expires_at_ts', '>', cur_ts)
50
+ .update({
51
+ expires_at_ts: cur_ts + expiration_seconds
52
+ })
53
+ if(updated == 0)
54
+ resolve(false)
55
+ else
56
+ resolve(true)
57
+ }catch(e){
58
+ reject(e)
59
+ }
60
+ });
61
+ }
62
+
63
+ static delete(table_name, row_id, client_uuid){
64
+ return new Promise(async (resolve, reject) => {
65
+ try {
66
+ await db(table).where(
67
+ {table_name, row_id, client_uuid}
68
+ ).delete()
69
+ resolve(true)
70
+ }catch(e){
71
+ reject(e)
72
+ }
73
+ });
74
+ }
75
+
76
+ }
77
+
78
+ module.exports = EditLocks
@@ -19,10 +19,13 @@ class Investigations {
19
19
  try{
20
20
  const sql = `SELECT
21
21
  id, uuid,
22
- array_to_string(
23
- regexp_split_to_array(?,'[^[:alnum:]_/:]+')
24
- ,','
25
- ) as label
22
+ array_to_string(ARRAY(
23
+ SELECT token
24
+ FROM unnest(
25
+ regexp_split_to_array(dataset, '[^[:alnum:]_/:]+')
26
+ ) AS token
27
+ WHERE token ILIKE ?
28
+ ), ',') AS label
26
29
  FROM investigations
27
30
  WHERE dataset_search @@ plainto_tsquery('simple', ?)
28
31
  OR dataset ILIKE ?
@@ -31,7 +34,7 @@ class Investigations {
31
34
  const tsQuery = `${text}:*`;
32
35
  const ilikeQuery = `%${text}%`;
33
36
  const result = await db.raw(sql,
34
- [txtQuery, tsQuery, ilikeQuery, limit, offset]
37
+ [ilikeQuery, tsQuery, ilikeQuery, limit, offset]
35
38
  );
36
39
  resolve(result.rows)
37
40
  }catch(e){
@@ -58,19 +61,22 @@ class Investigations {
58
61
  item.format = item.format || 'turtle'
59
62
  return new Promise(async (resolve, reject) => {
60
63
  try {
64
+ let _id = null
61
65
  const existing = await db(table)
62
66
  .where({uuid: item.uuid})
63
67
  .first();
64
68
  let operation = existing ? 'UPDATE' : 'INSERT';
65
69
  if(existing){
70
+ delete item["id"]
66
71
  await db(table)
67
72
  .where({uuid: item.uuid})
68
73
  .update(item);
69
74
  }else{
70
- await db(table).insert(item);
75
+ const [{id}] = await db(table).insert(item).returning('id');
76
+ _id = id;
71
77
  }
72
78
  resolve({
73
- success: true, operation
79
+ success: true, operation, data: _id
74
80
  })
75
81
  }catch(e){
76
82
  reject(e)