coreapi: Basic object API implementation
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
Łukasz Magiera committed
Dec 14, 2017 at 23:55 UTC
529f84726e8705947adad4bdac9af1196bea841a
3 files changed
+217
-21
core/coreapi/coreapi.go
+4
@@ -41,6 +41,10 @@ func (api *CoreAPI) Key() coreiface.KeyAPI {
41
return &KeyAPI{api, nil}
42
}
43
44
+func (api *CoreAPI) Object() coreiface.ObjectAPI {
45
+ return (*ObjectAPI)(api)
46
+}
47
+
48
// ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the
49
// resolved Node.
50
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
core/coreapi/interface/interface.go
+23
-21
@@ -191,27 +191,29 @@ type KeyAPI interface {
191
Remove(ctx context.Context, name string) (Path, error)
192
}
193
194
-// type ObjectAPI interface {
195
-// New() (cid.Cid, Object)
196
-// Get(string) (Object, error)
197
-// Links(string) ([]*Link, error)
198
-// Data(string) (Reader, error)
199
-// Stat(string) (ObjectStat, error)
200
-// Put(Object) (cid.Cid, error)
201
-// SetData(string, Reader) (cid.Cid, error)
202
-// AppendData(string, Data) (cid.Cid, error)
203
-// AddLink(string, string, string) (cid.Cid, error)
204
-// RmLink(string, string) (cid.Cid, error)
205
-// }
206
-
207
-// type ObjectStat struct {
208
-// Cid cid.Cid
209
-// NumLinks int
210
-// BlockSize int
211
-// LinksSize int
212
-// DataSize int
213
-// CumulativeSize int
214
-// }
194
+//TODO: Should this use paths instead of cids?
195
+type ObjectAPI interface {
196
+ New(ctx context.Context) (Node, error)
197
+ Put(context.Context, Node) error
198
+ Get(context.Context, Path) (Node, error)
199
+ Data(context.Context, Path) (io.Reader, error)
200
+ Links(context.Context, Path) ([]*Link, error)
201
+ Stat(context.Context, Path) (*ObjectStat, error)
202
+
203
+ AddLink(ctx context.Context, base Path, name string, child Path, create bool) (Node, error) //TODO: make create optional
204
+ RmLink(context.Context, Path, string) (Node, error)
205
+ AppendData(context.Context, Path, io.Reader) (Node, error)
206
+ SetData(context.Context, Path, io.Reader) (Node, error)
207
+}
208
+
209
+type ObjectStat struct {
210
+ Cid *cid.Cid
211
+ NumLinks int
212
+ BlockSize int
213
+ LinksSize int
214
+ DataSize int
215
+ CumulativeSize int
216
+}
217
218
var ErrIsDir = errors.New("object is a directory")
219
var ErrOffline = errors.New("can't resolve, ipfs node is offline")
core/coreapi/object.go
new
+190
@@ -0,0 +1,190 @@
1
+package coreapi
2
+
3
+import (
4
+ "bytes"
5
+ "context"
6
+ "errors"
7
+ "io"
8
+ "io/ioutil"
9
+
10
+ "github.com/ipfs/go-ipfs/merkledag/utils"
11
+
12
+ coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
13
+ dag "github.com/ipfs/go-ipfs/merkledag"
14
+ ft "github.com/ipfs/go-ipfs/unixfs"
15
+)
16
+
17
+type ObjectAPI CoreAPI
18
+
19
+func (api *ObjectAPI) New(ctx context.Context) (coreiface.Node, error) {
20
+ node := new(dag.ProtoNode)
21
+
22
+ _, err := api.node.DAG.Add(node)
23
+ if err != nil {
24
+ return nil, err
25
+ }
26
+ return node, nil
27
+}
28
+
29
+func (api *ObjectAPI) Put(context.Context, coreiface.Node) error {
30
+ return errors.New("todo") // TODO: what should this method take? Should we just redir to dag-put?f
31
+}
32
+
33
+func (api *ObjectAPI) Get(ctx context.Context, path coreiface.Path) (coreiface.Node, error) {
34
+ return api.core().ResolveNode(ctx, path)
35
+}
36
+
37
+func (api *ObjectAPI) Data(ctx context.Context, path coreiface.Path) (io.Reader, error) {
38
+ nd, err := api.core().ResolveNode(ctx, path)
39
+ if err != nil {
40
+ return nil, err
41
+ }
42
+
43
+ pbnd, ok := nd.(*dag.ProtoNode)
44
+ if !ok {
45
+ return nil, dag.ErrNotProtobuf
46
+ }
47
+
48
+ return bytes.NewReader(pbnd.Data()), nil
49
+}
50
+
51
+func (api *ObjectAPI) Links(ctx context.Context, path coreiface.Path) ([]*coreiface.Link, error) {
52
+ nd, err := api.core().ResolveNode(ctx, path)
53
+ if err != nil {
54
+ return nil, err
55
+ }
56
+
57
+ links := nd.Links()
58
+ out := make([]*coreiface.Link, len(links))
59
+ for n, l := range links {
60
+ out[n] = (*coreiface.Link)(l)
61
+ }
62
+
63
+ return out, nil
64
+}
65
+
66
+func (api *ObjectAPI) Stat(ctx context.Context, path coreiface.Path) (*coreiface.ObjectStat, error) {
67
+ nd, err := api.core().ResolveNode(ctx, path)
68
+ if err != nil {
69
+ return nil, err
70
+ }
71
+
72
+ stat, err := nd.Stat()
73
+ if err != nil {
74
+ return nil, err
75
+ }
76
+
77
+ out := &coreiface.ObjectStat{
78
+ Cid: nd.Cid(),
79
+ NumLinks: stat.NumLinks,
80
+ BlockSize: stat.BlockSize,
81
+ LinksSize: stat.LinksSize,
82
+ DataSize: stat.DataSize,
83
+ CumulativeSize: stat.CumulativeSize,
84
+ }
85
+
86
+ return out, nil
87
+}
88
+
89
+func (api *ObjectAPI) AddLink(ctx context.Context, base coreiface.Path, name string, child coreiface.Path, create bool) (coreiface.Node, error) {
90
+ rootNd, err := api.core().ResolveNode(ctx, base)
91
+ if err != nil {
92
+ return nil, err
93
+ }
94
+
95
+ childNd, err := api.core().ResolveNode(ctx, child)
96
+ if err != nil {
97
+ return nil, err
98
+ }
99
+
100
+ rootPb, ok := rootNd.(*dag.ProtoNode)
101
+ if !ok {
102
+ return nil, dag.ErrNotProtobuf
103
+ }
104
+
105
+ var createfunc func() *dag.ProtoNode
106
+ if create {
107
+ createfunc = ft.EmptyDirNode
108
+ }
109
+
110
+ e := dagutils.NewDagEditor(rootPb, api.node.DAG)
111
+
112
+ err = e.InsertNodeAtPath(ctx, name, childNd, createfunc)
113
+ if err != nil {
114
+ return nil, err
115
+ }
116
+
117
+ nnode, err := e.Finalize(api.node.DAG)
118
+ if err != nil {
119
+ return nil, err
120
+ }
121
+
122
+ return nnode, nil
123
+}
124
+
125
+func (api *ObjectAPI) RmLink(ctx context.Context, root coreiface.Path, link string) (coreiface.Node, error) {
126
+ rootNd, err := api.core().ResolveNode(ctx, root)
127
+ if err != nil {
128
+ return nil, err
129
+ }
130
+
131
+ rootPb, ok := rootNd.(*dag.ProtoNode)
132
+ if !ok {
133
+ return nil, dag.ErrNotProtobuf
134
+ }
135
+
136
+ e := dagutils.NewDagEditor(rootPb, api.node.DAG)
137
+
138
+ err = e.RmLink(ctx, link)
139
+ if err != nil {
140
+ return nil, err
141
+ }
142
+
143
+ nnode, err := e.Finalize(api.node.DAG)
144
+ if err != nil {
145
+ return nil, err
146
+ }
147
+
148
+ return nnode, nil
149
+}
150
+
151
+func (api *ObjectAPI) AppendData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.Node, error) {
152
+ return api.patchData(ctx, path, r, true)
153
+}
154
+
155
+func (api *ObjectAPI) SetData(ctx context.Context, path coreiface.Path, r io.Reader) (coreiface.Node, error) {
156
+ return api.patchData(ctx, path, r, false)
157
+}
158
+
159
+func (api *ObjectAPI) patchData(ctx context.Context, path coreiface.Path, r io.Reader, appendData bool) (coreiface.Node, error) {
160
+ nd, err := api.core().ResolveNode(ctx, path)
161
+ if err != nil {
162
+ return nil, err
163
+ }
164
+
165
+ pbnd, ok := nd.(*dag.ProtoNode)
166
+ if !ok {
167
+ return nil, dag.ErrNotProtobuf
168
+ }
169
+
170
+ data, err := ioutil.ReadAll(r)
171
+ if err != nil {
172
+ return nil, err
173
+ }
174
+
175
+ if appendData {
176
+ data = append(pbnd.Data(), data...)
177
+ }
178
+ pbnd.SetData(data)
179
+
180
+ _, err = api.node.DAG.Add(pbnd)
181
+ if err != nil {
182
+ return nil, err
183
+ }
184
+
185
+ return pbnd, nil
186
+}
187
+
188
+func (api *ObjectAPI) core() coreiface.CoreAPI {
189
+ return (*CoreAPI)(api)
190
+}