Browse Source

outline repository pattern

Kieran Gibb 1 year ago
parent
commit
0698a97f05
No known key found for this signature in database
5 changed files with 127 additions and 109 deletions
  1. 86
    67
      index.js
  2. 2
    2
      package.json
  3. 26
    33
      test/index.test.js
  4. 7
    1
      util.js
  5. 6
    6
      yarn.lock

+ 86
- 67
index.js View File

@@ -2,11 +2,11 @@ const Config = require('cobox-config')
2 2
 const crypto = require('cobox-crypto')
3 3
 const path = require('path')
4 4
 const thunky = require('thunky')
5
-const debug = require('debug')('cobox-server')
5
+const debug = require('debug')('cobox-group-store')
6 6
 const constants = require('cobox-constants')
7 7
 const maybe = require('call-me-maybe')
8 8
 
9
-const { isString } = require('./util')
9
+const { uniq, isString } = require('./util')
10 10
 
11 11
 module.exports = (createGroup, storage, opts) => new GroupStore(createGroup, storage, opts)
12 12
 
@@ -19,18 +19,10 @@ class GroupStore {
19 19
     this.opts = opts
20 20
     this.createGroup = createGroup
21 21
 
22
-    this.groups = this.config.groups.list().reduce((collection, groupOpts) => {
23
-      var { address, encryptionKey } = groupOpts
24
-
25
-      var group = encryptionKey
26
-        ? this.createGroup(this.storage, address, Object.assign(this.opts, { encryptionKey }))
27
-        : this.createGroup(this.storage, address, this.opts)
28
-
29
-      collection[address] = group
30
-      collection[group.address] = group
31
-      if (group.name) collection[group.name] = group
32
-      return collection
33
-    }, {})
22
+    this.groups = {}
23
+    this.config.groups.list().forEach((groupOpts) => (
24
+      this._cacheGroup(this._buildGroup(groupOpts))
25
+    ))
34 26
 
35 27
     this._readyCallback = thunky(this._ready.bind(this))
36 28
   }
@@ -44,68 +36,60 @@ class GroupStore {
44 36
     }))
45 37
   }
46 38
 
47
-  all () {
48
-    var self = this
49
-    return new Promise((resolve, reject) => {
50
-      self.ready((err) => {
51
-        if (err) return reject(err)
52
-        resolve(Array.from(new Set(Object.values(self.groups))))
53
-      })
54
-    })
39
+  async all () {
40
+    try {
41
+      return uniq(Object.values(this.groups))
42
+    } catch (err) {
43
+      debug(err)
44
+      return err
45
+    }
55 46
   }
56 47
 
57
-  async get (id) {
48
+  // expects address to be a buffer...
49
+  // so we must sanitize params before using store
50
+  async where (params = {}) {
58 51
     try {
59
-      await this.ready()
60
-      var { address, name } = this._validate(id)
61
-
62
-      if (name) {
63
-        var group = this.groups[name]
64
-      } else if (address) {
65
-        var group = this.groups[address.toString('hex')]
66
-        var groupOpts = this.config.groups[address]
67
-        if (groupOpts && name && groupOpts.name !== name) throw new Error('invalid: group with this name already exists')
52
+      var response = await this.all()
53
+      var keys = Object.keys(params)
54
+
55
+      for (var i = 0; i < keys.length; ++i) {
56
+        var key = keys[i]
57
+        response = response.filter((entry) => {
58
+          var found = entry[key] === params[key]
59
+          return found
60
+        })
68 61
       }
69 62
 
70
-      if (group) {
71
-        await group.ready()
72
-        return group
73
-      }
63
+      return response
64
+    } catch (err) {
65
+      debug(err)
66
+      return err
67
+    }
68
+  }
74 69
 
75
-      return null
76
-    } catch (error) {
77
-      debug(error)
78
-      return error
70
+  async findBy (params = {}) {
71
+    try {
72
+      let response = await this.where(params)
73
+      return response[0]
74
+    } catch (err) {
75
+      debug(err)
76
+      return err
79 77
     }
80 78
   }
81 79
 
82
-  async create (id) {
80
+  async create (params = {}) {
83 81
     try {
84
-      await this.ready()
85
-      var group = await this.get(id)
82
+      var group = await this.findBy(params)
86 83
       if (group) throw new Error('group already exists')
87 84
 
88
-      var name = id
89
-      var address = crypto.address()
90
-      var encryptionKey = crypto.encryptionKey()
91
-
92
-      var group = this.createGroup(
93
-        this.storage,
94
-        address,
95
-        Object.assign(this.opts, { encryptionKey })
96
-      )
97
-
98
-      this.groups[address.toString('hex')] = group
99
-      this.groups[address] = group
100
-      if (name) this.groups[name] = group
101
-
102
-      var groupOpts = group.keys
103
-      if (name) groupOpts.name = name
104
-      this.config.groups.set(group.publicKey, groupOpts)
105
-      this.config.save()
85
+      var { name, address, encryptionKey } = params
86
+      var group = this.buildGroup(params)
106 87
 
107 88
       await group.ready()
108 89
 
90
+      this._cacheGroup(group)
91
+      this._saveGroup(group)
92
+
109 93
       return group
110 94
     } catch (error) {
111 95
       debug(error)
@@ -113,6 +97,10 @@ class GroupStore {
113 97
     }
114 98
   }
115 99
 
100
+  async destroy (id) {
101
+
102
+  }
103
+
116 104
   // ------------------------------------------------------------------
117 105
 
118 106
   _ready (callback) {
@@ -141,12 +129,43 @@ class GroupStore {
141 129
     next(0)
142 130
   }
143 131
 
144
-  _validate (id) {
145
-    if (crypto.isKey(id)) key = id
146
-    if (!crypto.isKey(id) && isString(id)) name = id
147
-    // Ensure we either have no key, or the key is a valid key
148
-    if (!(!key || crypto.isKey(key))) throw new Error('invalid: key format')
149
-    return { key, name }
132
+  _buildGroup (params = {}) {
133
+    return this.createGroup(
134
+      this.storage,
135
+      Buffer.from(params.address),
136
+      Object.assign(this.opts, {
137
+        encryptionKey: Buffer.from(params.encryptionKey),
138
+        name: params.name
139
+      })
140
+    )
141
+  }
142
+
143
+  _cacheGroup (group) {
144
+    this.groups[group.address.toString('hex')] = group
145
+    this.groups[group.address] = group
146
+    if (group.name) this.groups[group.name] = group
147
+    return true
148
+  }
149
+
150
+  _saveGroup (group) {
151
+    this.config.groups.set(group.address, group._attributes())
152
+    var success = this.config.save()
153
+    if (!success) return false
154
+
155
+    Object.keys(group._attributes()).forEach((attr) => {
156
+      this.groups[attr] = group
157
+    })
158
+
159
+    return true
150 160
   }
151 161
 
152 162
 }
163
+
164
+// _validate (id) {
165
+//   if (crypto.isKey(id)) key = id
166
+//   if (!crypto.isKey(id) && isString(id)) name = id
167
+//   // Ensure we either have no key, or the key is a valid key
168
+//   if (!(!key || crypto.isKey(key))) throw new Error('invalid: key format')
169
+//   return { key, name }
170
+// }
171
+

+ 2
- 2
package.json View File

@@ -13,9 +13,9 @@
13 13
   "license": "AGPL-3.0-or-later",
14 14
   "dependencies": {
15 15
     "call-me-maybe": "^1.0.1",
16
-    "cobox-crypto": "git+https://ledger-git.dyne.org/cobox/cobox-crypto#development",
17
-    "cobox-constants": "^1.0.0",
18 16
     "cobox-config": "git+https://ledger-git.dyne.org/cobox/cobox-config#development",
17
+    "cobox-constants": "^1.0.0",
18
+    "cobox-crypto": "git+https://ledger-git.dyne.org/cobox/cobox-crypto#development",
19 19
     "cobox-group": "git+https://ledger-git.dyne.org/cobox/cobox-group#development",
20 20
     "debug": "^4.1.1",
21 21
     "thunky": "^1.1.0"

+ 26
- 33
test/index.test.js View File

@@ -7,64 +7,57 @@ const Store = require('../')
7 7
 const { tmp, cleanup } = require('./util')
8 8
 
9 9
 describe('group store: basic', (context) => {
10
-  context('on load it caches existing groups from the config', function (assert, next) {
10
+  context('ready()', async function (assert, next) {
11 11
     let storage = tmp(),
12 12
       config = Config(storage),
13
-      group = crypto.keySet()
13
+      params = crypto.keySet()
14 14
 
15
-    config.groups.set(group.address, group)
15
+    config.groups.set(params.address, params)
16 16
 
17 17
     const store = Store(Decrypted, storage, { config })
18 18
 
19
-    store.ready((err) => {
20
-      assert.error(err, 'no error')
19
+    await store.ready()
21 20
 
22
-      assert.ok(Object.values(store.groups).length, 'builds then caches groups from the config')
23
-      assert.ok(store.groups[group.address], 'accessible using buffer')
24
-      assert.ok(store.groups[group.address.toString('hex')], 'accessible using hex')
21
+    assert.ok(Object.values(store.groups).length, 'builds then caches groups from the config')
22
+    assert.ok(store.groups[params.address], 'accessible using buffer')
23
+    assert.ok(store.groups[params.address.toString('hex')], 'accessible using hex')
25 24
 
26
-      cleanup(storage, next)
27
-    })
25
+    cleanup(storage, next)
28 26
   })
29 27
 
30
-  context('create a single group', async function (assert, next) {
28
+  context('where()', async function (assert, next) {
31 29
     let storage = tmp(),
32
-      config = Config(storage)
33
-
34
-    const store = Store(Decrypted, storage, { config })
30
+      config = Config(storage),
31
+      params = crypto.keySet()
35 32
 
36
-    var a = await store.get()
37
-    assert.ok(a, 'creates a new group')
38
-    cleanup(storage, next)
39
-  })
33
+    Object.keys(params).forEach((key) => {
34
+      if (Buffer.isBuffer(params[key])) params[key] = Buffer.from(params[key])
35
+    })
40 36
 
41
-  context('get a group by key', async function (assert, next) {
42
-    let storage = tmp(),
43
-      config = Config(storage)
37
+    config.groups.set(params.address, params)
38
+    config.save()
39
+    config.load()
44 40
 
45 41
     const store = Store(Decrypted, storage, { config })
46 42
 
47
-    var a = await store.get()
48
-    assert.ok(a, 'creates a new group')
43
+    await store.ready()
44
+
45
+    var response = await store.where(params)
46
+
47
+    assert.same(response.address, params.address, 'finds a group')
49 48
 
50
-    var b = await store.get(a.key)
51
-    assert.same(a.key, b.key, 'getes the same group by key')
52
-    assert.same(a._id, b._id, 'is the same group instance')
53 49
     cleanup(storage, next)
54 50
   })
55 51
 
56
-  context('get a group by name', async function (assert, next) {
52
+  context('create()', async function (assert, next) {
57 53
     let storage = tmp(),
58
-      config = Config(storage)
54
+      config = Config(storage),
55
+      params = Object.assign(crypto.keySet(), { name: 'magma' })
59 56
 
60 57
     const store = Store(Decrypted, storage, { config })
58
+    var a = await store.create(params)
61 59
 
62
-    var a = await store.get("Silly String")
63 60
     assert.ok(a, 'creates a new group')
64
-
65
-    var b = await store.get("Silly String")
66
-    assert.same(a.key, b.key, 'getes the same group by key')
67
-    assert.same(a._id, b._id, 'is the same group instance')
68 61
     cleanup(storage, next)
69 62
   })
70 63
 })

+ 7
- 1
util.js View File

@@ -6,6 +6,11 @@ function isFunction (variable) {
6 6
   return variable && typeof variable === 'function'
7 7
 }
8 8
 
9
+function uniq (array) {
10
+  if (!Array.isArray(array)) array = [array]
11
+  return Array.from(new Set(array))
12
+}
13
+
9 14
 function removeEmpty (obj) {
10 15
   return Object.keys(obj)
11 16
     .filter(k => obj[k] != null)
@@ -19,6 +24,7 @@ function removeEmpty (obj) {
19 24
 module.exports = {
20 25
   isString,
21 26
   isFunction,
22
-  removeEmpty
27
+  removeEmpty,
28
+  uniq
23 29
 }
24 30
 

+ 6
- 6
yarn.lock View File

@@ -377,8 +377,8 @@ cobox-constants@^1.0.0:
377 377
 
378 378
 "cobox-crypto@git+https://ledger-git.dyne.org/CoBox/cobox-crypto#development":
379 379
   version "1.1.0"
380
-  uid "0ee347eaf84f88deca469185061be825a86f7fe9"
381
-  resolved "git+https://ledger-git.dyne.org/CoBox/cobox-crypto#0ee347eaf84f88deca469185061be825a86f7fe9"
380
+  uid "7190373065de44fc7c4a2d6c20dfbfbe429d06e6"
381
+  resolved "git+https://ledger-git.dyne.org/CoBox/cobox-crypto#7190373065de44fc7c4a2d6c20dfbfbe429d06e6"
382 382
   dependencies:
383 383
     assert "^2.0.0"
384 384
     bip39 "^3.0.2"
@@ -387,7 +387,8 @@ cobox-constants@^1.0.0:
387 387
 
388 388
 "cobox-crypto@git+https://ledger-git.dyne.org/cobox/cobox-crypto#development":
389 389
   version "1.1.0"
390
-  resolved "git+https://ledger-git.dyne.org/cobox/cobox-crypto#0ee347eaf84f88deca469185061be825a86f7fe9"
390
+  uid "7190373065de44fc7c4a2d6c20dfbfbe429d06e6"
391
+  resolved "git+https://ledger-git.dyne.org/cobox/cobox-crypto#7190373065de44fc7c4a2d6c20dfbfbe429d06e6"
391 392
   dependencies:
392 393
     assert "^2.0.0"
393 394
     bip39 "^3.0.2"
@@ -396,8 +397,7 @@ cobox-constants@^1.0.0:
396 397
 
397 398
 "cobox-crypto@git+ssh://git@ledger-git.dyne.org:2240/CoBox/cobox-crypto.git#development":
398 399
   version "1.1.0"
399
-  uid "0ee347eaf84f88deca469185061be825a86f7fe9"
400
-  resolved "git+ssh://git@ledger-git.dyne.org:2240/CoBox/cobox-crypto.git#0ee347eaf84f88deca469185061be825a86f7fe9"
400
+  resolved "git+ssh://git@ledger-git.dyne.org:2240/CoBox/cobox-crypto.git#7190373065de44fc7c4a2d6c20dfbfbe429d06e6"
401 401
   dependencies:
402 402
     assert "^2.0.0"
403 403
     bip39 "^3.0.2"
@@ -406,7 +406,7 @@ cobox-constants@^1.0.0:
406 406
 
407 407
 "cobox-group@git+https://ledger-git.dyne.org/cobox/cobox-group#development":
408 408
   version "2.0.0"
409
-  resolved "git+https://ledger-git.dyne.org/cobox/cobox-group#b583d77cd37c60e41569abe16a62fbb3f98614e6"
409
+  resolved "git+https://ledger-git.dyne.org/cobox/cobox-group#0d348a3620fa8cfbb7287016aa75b57eacf1d415"
410 410
   dependencies:
411 411
     assert "^2.0.0"
412 412
     call-me-maybe "^1.0.1"