github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/applet/applet.go (about) 1 package applet 2 3 import ( 4 "context" 5 6 confid "github.com/machinefi/w3bstream/pkg/depends/conf/id" 7 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx" 8 "github.com/machinefi/w3bstream/pkg/depends/kit/statusx" 9 "github.com/machinefi/w3bstream/pkg/enums" 10 "github.com/machinefi/w3bstream/pkg/errors/status" 11 "github.com/machinefi/w3bstream/pkg/models" 12 "github.com/machinefi/w3bstream/pkg/modules/deploy" 13 "github.com/machinefi/w3bstream/pkg/modules/resource" 14 "github.com/machinefi/w3bstream/pkg/modules/strategy" 15 "github.com/machinefi/w3bstream/pkg/types" 16 "github.com/machinefi/w3bstream/pkg/types/wasm" 17 ) 18 19 func GetBySFID(ctx context.Context, id types.SFID) (*models.Applet, error) { 20 d := types.MustMgrDBExecutorFromContext(ctx) 21 m := &models.Applet{RelApplet: models.RelApplet{AppletID: id}} 22 23 if err := m.FetchByAppletID(d); err != nil { 24 if sqlx.DBErr(err).IsNotFound() { 25 return nil, status.AppletNotFound 26 } 27 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 28 } 29 return m, nil 30 } 31 32 func RemoveBySFID(ctx context.Context, id types.SFID) error { 33 d := types.MustMgrDBExecutorFromContext(ctx) 34 m := &models.Applet{RelApplet: models.RelApplet{AppletID: id}} 35 36 return sqlx.NewTasks(d).With( 37 func(d sqlx.DBExecutor) error { 38 if err := m.DeleteByAppletID(d); err != nil { 39 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 40 } 41 return nil 42 }, 43 func(d sqlx.DBExecutor) error { 44 ctx := types.WithMgrDBExecutor(ctx, d) 45 return strategy.Remove(ctx, &strategy.CondArgs{ 46 AppletIDs: types.SFIDs{id}, 47 }) 48 }, 49 func(d sqlx.DBExecutor) error { 50 ctx := types.WithMgrDBExecutor(ctx, d) 51 return deploy.RemoveByAppletSFID(ctx, m.AppletID) 52 }, 53 ).Do() 54 } 55 56 func Remove(ctx context.Context, r *CondArgs) error { 57 var ( 58 d = types.MustMgrDBExecutorFromContext(ctx) 59 m = &models.Applet{} 60 61 err error 62 lst []models.Applet 63 ) 64 65 return sqlx.NewTasks(d).With( 66 func(d sqlx.DBExecutor) error { 67 lst, err = m.List(d, r.Condition()) 68 if err != nil { 69 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 70 } 71 return nil 72 }, 73 func(d sqlx.DBExecutor) error { 74 ctx := types.WithMgrDBExecutor(ctx, d) 75 summary := statusx.ErrorFields{} 76 for i := range lst { 77 v := &lst[i] 78 if err = RemoveBySFID(ctx, v.AppletID); err != nil { 79 se := statusx.FromErr(err) 80 summary = append(summary, &statusx.ErrorField{ 81 In: v.AppletID.String(), 82 Field: se.Key, 83 Msg: se.Desc, 84 }) 85 } 86 } 87 if len(summary) > 0 { 88 return status.BatchRemoveAppletFailed.StatusErr(). 89 AppendErrorFields(summary...) 90 } 91 return nil 92 }, 93 ).Do() 94 } 95 96 func List(ctx context.Context, r *ListReq) (*ListRsp, error) { 97 var ( 98 d = types.MustMgrDBExecutorFromContext(ctx) 99 err error 100 app = &models.Applet{} 101 ret = &ListRsp{} 102 cond = r.Condition() 103 ) 104 105 if ret.Data, err = app.List(d, cond, r.Addition()); err != nil { 106 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 107 } 108 if ret.Total, err = app.Count(d, cond); err != nil { 109 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 110 } 111 return ret, nil 112 } 113 114 func ListDetail(ctx context.Context, r *ListReq) (*ListDetailRsp, error) { 115 var ( 116 lst *ListRsp 117 err error 118 ins *models.Instance 119 res *models.Resource 120 ret = &ListDetailRsp{} 121 ) 122 123 lst, err = List(ctx, r) 124 if err != nil { 125 return nil, err 126 } 127 ret = &ListDetailRsp{Total: lst.Total} 128 129 for i := range lst.Data { 130 app := &lst.Data[i] 131 if ins, _ = deploy.GetByAppletSFID(ctx, app.AppletID); ins == nil { 132 continue 133 } 134 if res, err = resource.GetBySFID(ctx, app.ResourceID); err != nil { 135 return nil, err 136 } 137 ret.Data = append(ret.Data, detail(app, ins, res)) 138 } 139 return ret, nil 140 } 141 142 func Create(ctx context.Context, r *CreateReq) (*CreateRsp, error) { 143 var ( 144 res *models.Resource 145 acc = types.MustAccountFromContext(ctx) 146 raw []byte 147 err error 148 ) 149 150 filename := r.WasmName 151 if filename == "" { 152 filename = r.AppletName + ".wasm" 153 } 154 res, raw, err = resource.Create(ctx, acc.AccountID, r.File, filename, r.WasmMd5) 155 if err != nil { 156 return nil, err 157 } 158 ctx = types.WithResource(ctx, res) 159 160 var ( 161 idg = confid.MustNewSFIDGenerator() 162 prj = types.MustProjectFromContext(ctx) 163 app = &models.Applet{ 164 RelApplet: models.RelApplet{AppletID: idg.MustGenSFID()}, 165 RelProject: models.RelProject{ProjectID: prj.ProjectID}, 166 RelResource: models.RelResource{ResourceID: res.ResourceID}, 167 AppletInfo: models.AppletInfo{Name: r.AppletName}, 168 } 169 sty []models.Strategy 170 ins *models.Instance 171 ) 172 173 err = sqlx.NewTasks(types.MustMgrDBExecutorFromContext(ctx)).With( 174 func(d sqlx.DBExecutor) error { 175 if err = app.Create(d); err != nil { 176 if sqlx.DBErr(err).IsConflict() { 177 return status.AppletNameConflict 178 } 179 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 180 } 181 ctx = types.WithApplet(ctx, app) 182 return nil 183 }, 184 func(d sqlx.DBExecutor) error { 185 ctx := types.WithMgrDBExecutor(ctx, d) 186 return strategy.BatchCreate(ctx, r.BuildStrategies(ctx)) 187 }, 188 func(d sqlx.DBExecutor) error { 189 if r.WasmCache == nil { 190 r.WasmCache = wasm.DefaultCache() 191 } 192 ctx := types.WithMgrDBExecutor(ctx, d) 193 rb := &deploy.CreateReq{Cache: r.WasmCache} 194 ins, err = deploy.UpsertByCode(ctx, rb, raw, enums.INSTANCE_STATE__STARTED) 195 return err 196 }, 197 ).Do() 198 if err != nil { 199 return nil, err 200 } 201 202 return &CreateRsp{ 203 Applet: app, 204 Instance: ins, 205 Resource: res, 206 Strategies: sty, 207 }, nil 208 } 209 210 func Update(ctx context.Context, r *UpdateReq) (*UpdateRsp, error) { 211 var ( 212 d = types.MustMgrDBExecutorFromContext(ctx) 213 app = types.MustAppletFromContext(ctx) 214 ins *models.Instance // maybe not deployed 215 res *models.Resource 216 sty []models.Strategy 217 raw []byte 218 err error 219 ) 220 221 // create resource if needed 222 if r.File != nil { 223 acc := types.MustAccountFromContext(ctx) 224 filename, md5 := r.Info.WasmName, r.Info.WasmMd5 225 if filename == "" { 226 filename = r.AppletName + ".wasm" 227 } 228 res, raw, err = resource.Create(ctx, acc.AccountID, r.File, filename, md5) 229 if err != nil { 230 return nil, err 231 } 232 } 233 234 err = sqlx.NewTasks(d).With( 235 // update strategy 236 func(d sqlx.DBExecutor) error { 237 ctx := types.WithMgrDBExecutor(ctx, d) 238 sty = r.BuildStrategies(ctx) 239 if len(sty) == 0 { 240 return nil 241 } 242 if err = strategy.Remove(ctx, &strategy.CondArgs{ 243 AppletIDs: types.SFIDs{app.AppletID}, 244 }); err != nil { 245 return err 246 } 247 if err = strategy.BatchCreate(ctx, r.BuildStrategies(ctx)); err != nil { 248 return err 249 } 250 return nil 251 }, 252 // update applet info 253 func(d sqlx.DBExecutor) error { 254 if r.Info.AppletName == "" && res == nil { 255 return nil 256 } 257 if r.Info.AppletName != "" { 258 app.Name = r.Info.AppletName 259 } 260 if res != nil { 261 app.ResourceID = res.ResourceID 262 } 263 if err = app.UpdateByAppletID(d); err != nil { 264 if sqlx.DBErr(err).IsConflict() { 265 return status.AppletNameConflict 266 } 267 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 268 } 269 return nil 270 }, 271 // update and deploy instance 272 func(d sqlx.DBExecutor) error { 273 ctx := types.WithMgrDBExecutor(ctx, d) 274 if r.File == nil { 275 return nil // instance state will not be changed 276 } 277 ins, err = deploy.GetByAppletSFID(ctx, app.AppletID) 278 if err != nil { 279 return err 280 } 281 var rb *deploy.CreateReq 282 if r.Info.WasmCache != nil { 283 rb = &deploy.CreateReq{Cache: r.Info.WasmCache} 284 } 285 ins, err = deploy.UpsertByCode(ctx, rb, raw, enums.INSTANCE_STATE__STARTED, ins.InstanceID) 286 return err 287 }, 288 ).Do() 289 if err != nil { 290 return nil, err 291 } 292 293 return &UpdateRsp{app, ins, res, sty}, nil 294 }