Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.

Commit 8305ab4

Browse files
author
Raphael Santo Domingo
committed
Refactor product smart contract with rust native implementations of product
Signed-off-by: Raphael Santo Domingo <[email protected]>
1 parent 3e841bd commit 8305ab4

File tree

3 files changed

+214
-260
lines changed

3 files changed

+214
-260
lines changed

contracts/product/src/handler.rs

+95-114
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ cfg_if! {
2727
}
2828
}
2929

30-
use grid_sdk::permissions::PermissionChecker;
31-
use grid_sdk::protos::product_payload::*;
32-
use grid_sdk::protos::product_state::Product;
30+
use grid_sdk::protocol::product::payload::{
31+
Action, ProductCreateAction, ProductDeleteAction, ProductPayload, ProductUpdateAction,
32+
};
33+
use grid_sdk::protocol::product::state::{ProductBuilder, ProductType};
34+
35+
use grid_sdk::protos::FromBytes;
3336

3437
use crate::addressing::*;
35-
use crate::payload::{Action, ProductPayload};
38+
use crate::payload::validate_payload;
3639
use crate::state::ProductState;
3740
use crate::validation::validate_gtin;
3841

@@ -76,11 +79,15 @@ impl ProductTransactionHandler {
7679

7780
fn create_product(
7881
&self,
79-
payload: ProductCreateAction,
80-
mut state: ProductState,
82+
payload: &ProductCreateAction,
83+
state: &mut ProductState,
8184
signer: &str,
82-
perm_checker: &PermissionChecker,
8385
) -> Result<(), ApplyError> {
86+
let product_id = payload.identifier();
87+
let owner = payload.owner();
88+
let product_type = payload.product_type();
89+
let properties = payload.properties();
90+
8491
// Check that the agent submitting the transactions exists in state
8592
let agent = match state.get_agent(signer)? {
8693
Some(agent) => agent,
@@ -100,47 +107,37 @@ impl ProductTransactionHandler {
100107
)));
101108
}
102109

103-
// Check that the agent has the pike permission "can_create_product" for the organization
104-
check_permission(perm_checker, signer, "can_create_product")?;
110+
// Check if product exists in state
111+
if state.get_product(product_id)?.is_some() {
112+
return Err(ApplyError::InvalidTransaction(format!(
113+
"Product already exists: {}",
114+
product_id,
115+
)));
116+
}
105117

106118
// Check if the product type is a GS1 product
107-
if payload.get_product_type() != ProductCreateAction_ProductType::GS1 {
119+
if product_type != &ProductType::GS1 {
108120
return Err(ApplyError::InvalidTransaction(
109121
"Invalid product type enum for product".to_string(),
110122
));
111123
}
112-
// Use this varible to pass in the type correct enum (product_state) on product create
113-
let product_type = grid_sdk::protos::product_state::Product_ProductType::GS1;
114124

115125
// Check if product identifier is a valid gtin
116-
let product_id = payload.get_identifier();
117126
if let Err(e) = validate_gtin(product_id) {
118127
return Err(ApplyError::InvalidTransaction(e.to_string()));
119128
}
120129

121130
// Check that the org owns the GS1 company prefix in the identifier
122-
let org = match state.get_organization(payload.get_owner())? {
131+
let org = match state.get_organization(payload.owner())? {
123132
Some(org) => org,
124133
None => {
125134
return Err(ApplyError::InvalidTransaction(format!(
126-
"The Agents organization does not exist: {}",
127-
signer
135+
"The Agent's organization does not exist: {}",
136+
signer,
128137
)));
129138
}
130139
};
131140

132-
// Check if product exists in state
133-
match state.get_product(product_id) {
134-
Ok(Some(_)) => {
135-
return Err(ApplyError::InvalidTransaction(format!(
136-
"Product already exists: {}",
137-
product_id
138-
)));
139-
}
140-
Ok(None) => (),
141-
Err(err) => return Err(err),
142-
}
143-
144141
/* Check if the agents organization contain GS1 Company Prefix key in its metadata
145142
(gs1_company_prefixes), and the prefix must match the company prefix in the identifier */
146143
let gs1_company_prefix_vec = org.metadata().to_vec();
@@ -167,25 +164,31 @@ impl ProductTransactionHandler {
167164
}
168165
}
169166

170-
let mut new_product = Product::new();
171-
new_product.set_identifier(product_id.to_string());
172-
new_product.set_owner(payload.get_owner().to_string());
173-
new_product.set_field_type(product_type);
174-
new_product.set_product_values(protobuf::RepeatedField::from_vec(
175-
payload.get_properties().to_vec(),
176-
));
167+
let new_product = ProductBuilder::new()
168+
.with_identifier(product_id.to_string())
169+
.with_owner(owner.to_string())
170+
.with_product_type(product_type.clone())
171+
.with_product_values(properties.to_vec())
172+
.build()
173+
.map_err(|err| {
174+
ApplyError::InvalidTransaction(format!("Cannot build product: {}", err))
175+
})?;
176+
177+
state.set_product(product_id, new_product)?;
177178

178-
state.set_product(signer, new_product)?;
179179
Ok(())
180180
}
181181

182182
fn update_product(
183183
&self,
184-
payload: ProductUpdateAction,
185-
mut state: ProductState,
184+
payload: &ProductUpdateAction,
185+
state: &mut ProductState,
186186
signer: &str,
187-
perm_checker: &PermissionChecker,
188187
) -> Result<(), ApplyError> {
188+
let product_id = payload.identifier();
189+
let product_type = payload.product_type();
190+
let properties = payload.properties();
191+
189192
// Check that the agent submitting the transactions exists in state
190193
let agent = match state.get_agent(signer)? {
191194
Some(agent) => agent,
@@ -197,22 +200,23 @@ impl ProductTransactionHandler {
197200
}
198201
};
199202

203+
// Check that the agent has an organization associated with it
204+
if agent.org_id().is_empty() {
205+
return Err(ApplyError::InvalidTransaction(format!(
206+
"The signing Agent does not have an associated organization: {}",
207+
signer
208+
)));
209+
}
210+
200211
// Check if the product type is a GS1 product
201-
let product_type = payload.get_product_type();
202-
if product_type != ProductUpdateAction_ProductType::GS1 {
212+
if product_type != &ProductType::GS1 {
203213
return Err(ApplyError::InvalidTransaction(
204214
"Invalid product type enum for product".to_string(),
205215
));
206216
}
207-
let product_id = payload.get_identifier();
208-
209-
// Check if product identifier is a valid gtin
210-
if let Err(e) = validate_gtin(product_id) {
211-
return Err(ApplyError::InvalidTransaction(e.to_string()));
212-
}
213217

214218
// Check if product exists
215-
let mut product = match state.get_product(product_id) {
219+
let product = match state.get_product(product_id) {
216220
Ok(Some(product)) => Ok(product),
217221
Ok(None) => Err(ApplyError::InvalidTransaction(format!(
218222
"No product exists: {}",
@@ -222,31 +226,43 @@ impl ProductTransactionHandler {
222226
}?;
223227

224228
// Check if the agent updating the product is part of the organization associated with the product
225-
if product.get_owner() != agent.org_id() {
229+
if product.owner() != agent.org_id() {
226230
return Err(ApplyError::InvalidTransaction(
227231
"Invalid organization for the agent submitting this transaction".to_string(),
228232
));
229233
}
230234

231-
// Check that the agent has the pike permission "can_update_product" for the organization
232-
check_permission(perm_checker, signer, "can_update_product")?;
235+
// Check if product identifier is a valid gtin
236+
if let Err(e) = validate_gtin(product_id) {
237+
return Err(ApplyError::InvalidTransaction(e.to_string()));
238+
}
233239

234240
// Handle updating the product
235-
let updated_product_values = payload.properties.clone();
236-
product.set_product_values(updated_product_values);
241+
let updated_product = ProductBuilder::new()
242+
.with_identifier(product_id.to_string())
243+
.with_owner(product.owner().to_string())
244+
.with_product_type(product_type.clone())
245+
.with_product_values(properties.to_vec())
246+
.build()
247+
.map_err(|err| {
248+
ApplyError::InvalidTransaction(format!("Cannot build product: {}", err))
249+
})?;
250+
251+
state.set_product(product_id, updated_product)?;
237252

238-
state.set_product(product_id, product)?;
239253
Ok(())
240254
}
241255

242256
fn delete_product(
243257
&self,
244-
payload: ProductDeleteAction,
245-
mut state: ProductState,
258+
payload: &ProductDeleteAction,
259+
state: &mut ProductState,
246260
signer: &str,
247-
perm_checker: &PermissionChecker,
248261
) -> Result<(), ApplyError> {
249-
// Check that the agent (signer) submitting the transactions exists in state
262+
let product_id = payload.identifier();
263+
let product_type = payload.product_type();
264+
265+
// Check that the agent submitting the transactions exists in state
250266
let agent = match state.get_agent(signer)? {
251267
Some(agent) => agent,
252268
None => {
@@ -258,18 +274,11 @@ impl ProductTransactionHandler {
258274
};
259275

260276
// Check if the product type is a GS1 product
261-
let product_type = payload.get_product_type();
262-
if product_type != ProductDeleteAction_ProductType::GS1 {
277+
if product_type != &ProductType::GS1 {
263278
return Err(ApplyError::InvalidTransaction(
264279
"Invalid product type enum for product".to_string(),
265280
));
266281
}
267-
let product_id = payload.get_identifier();
268-
269-
// Check if product identifier is a valid gtin
270-
if let Err(e) = validate_gtin(product_id) {
271-
return Err(ApplyError::InvalidTransaction(e.to_string()));
272-
}
273282

274283
// Check if product exists in state
275284
let product = match state.get_product(product_id) {
@@ -281,16 +290,18 @@ impl ProductTransactionHandler {
281290
Err(err) => Err(err),
282291
}?;
283292

293+
// Check if product identifier is a valid gtin
294+
if let Err(e) = validate_gtin(product_id) {
295+
return Err(ApplyError::InvalidTransaction(e.to_string()));
296+
}
297+
284298
// Check that the owner of the products organization is the same as the agent trying to delete the product
285-
if product.get_owner() != agent.org_id() {
299+
if product.owner() != agent.org_id() {
286300
return Err(ApplyError::InvalidTransaction(
287301
"Invalid organization for the agent submitting this transaction".to_string(),
288302
));
289303
}
290304

291-
// Check that the agent deleting the product has the "can_delete_product" permission for the organization
292-
check_permission(perm_checker, signer, "can_delete_product")?;
293-
294305
// Delete the product
295306
state.remove_product(product_id)?;
296307
Ok(())
@@ -315,62 +326,32 @@ impl TransactionHandler for ProductTransactionHandler {
315326
request: &TpProcessRequest,
316327
context: &mut dyn TransactionContext,
317328
) -> Result<(), ApplyError> {
318-
let payload = ProductPayload::new(request.get_payload());
319-
let payload = match payload {
320-
Err(e) => return Err(e),
321-
Ok(payload) => payload,
322-
};
323-
let payload = match payload {
324-
Some(x) => x,
325-
None => {
326-
return Err(ApplyError::InvalidTransaction(String::from(
327-
"Request must contain a payload",
328-
)));
329-
}
330-
};
329+
let payload = ProductPayload::from_bytes(request.get_payload()).map_err(|err| {
330+
ApplyError::InvalidTransaction(format!("Cannot build product payload: {}", err))
331+
})?;
331332

332-
if payload.get_timestamp().to_string().is_empty() {
333-
return Err(ApplyError::InvalidTransaction(String::from(
334-
"Timestamp is not set",
335-
)));
336-
}
333+
validate_payload(&payload)?;
337334

338335
info!(
339336
"Grid Product Payload {:?} {}",
340-
payload.get_action(),
341-
payload.get_timestamp()
337+
payload.action(),
338+
payload.timestamp(),
342339
);
343340

344341
let signer = request.get_header().get_signer_public_key();
345-
let state = ProductState::new(context);
346-
let perm_checker = PermissionChecker::new(context);
342+
let mut state = ProductState::new(context);
347343

348-
match payload.get_action() {
349-
Action::CreateProduct(create_product_payload) => {
350-
self.create_product(create_product_payload, state, signer, &perm_checker)?
344+
match payload.action() {
345+
Action::ProductCreate(create_product_payload) => {
346+
self.create_product(create_product_payload, &mut state, signer)?
351347
}
352-
Action::UpdateProduct(update_product_payload) => {
353-
self.update_product(update_product_payload, state, signer, &perm_checker)?
348+
Action::ProductUpdate(update_product_payload) => {
349+
self.update_product(update_product_payload, &mut state, signer)?
354350
}
355-
Action::DeleteProduct(delete_product_payload) => {
356-
self.delete_product(delete_product_payload, state, signer, &perm_checker)?
351+
Action::ProductDelete(delete_product_payload) => {
352+
self.delete_product(delete_product_payload, &mut state, signer)?
357353
}
358354
}
359355
Ok(())
360356
}
361357
}
362-
363-
fn check_permission(
364-
perm_checker: &PermissionChecker,
365-
signer: &str,
366-
permission: &str,
367-
) -> Result<(), ApplyError> {
368-
match perm_checker.has_permission(signer, permission) {
369-
Ok(true) => Ok(()),
370-
Ok(false) => Err(ApplyError::InvalidTransaction(format!(
371-
"The signer does not have the {} permission: {}.",
372-
permission, signer,
373-
))),
374-
Err(e) => Err(ApplyError::InvalidTransaction(format!("{}", e))),
375-
}
376-
}

0 commit comments

Comments
 (0)