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

Commit 7b6aa62

Browse files
author
Raphael Santo Domingo
committed
Refactor product smart contract
Other changes - Moved the 'check_permission' function call before checking for an agent's associated org Signed-off-by: Raphael Santo Domingo <[email protected]>
1 parent 12e6fd9 commit 7b6aa62

File tree

3 files changed

+225
-242
lines changed

3 files changed

+225
-242
lines changed

contracts/product/src/handler.rs

+104-94
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ cfg_if! {
2828
}
2929

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

3438
use crate::addressing::*;
35-
use crate::payload::{Action, ProductPayload};
39+
use crate::payload::validate_payload;
3640
use crate::state::ProductState;
3741
use crate::validation::validate_gtin;
3842

@@ -76,11 +80,16 @@ impl ProductTransactionHandler {
7680

7781
fn create_product(
7882
&self,
79-
payload: ProductCreateAction,
80-
mut state: ProductState,
83+
payload: &ProductCreateAction,
84+
state: &mut ProductState,
8185
signer: &str,
8286
perm_checker: &PermissionChecker,
8387
) -> Result<(), ApplyError> {
88+
let product_id = payload.identifier();
89+
let owner = payload.owner();
90+
let product_type = payload.product_type();
91+
let properties = payload.properties();
92+
8493
// Check that the agent submitting the transactions exists in state
8594
let agent = match state.get_agent(signer)? {
8695
Some(agent) => agent,
@@ -92,6 +101,9 @@ impl ProductTransactionHandler {
92101
}
93102
};
94103

104+
// Check signing agent's permission
105+
check_permission(perm_checker, signer, "can_create_product")?;
106+
95107
// Check that the agent has an organization associated with it
96108
if agent.org_id().is_empty() {
97109
return Err(ApplyError::InvalidTransaction(format!(
@@ -100,47 +112,37 @@ impl ProductTransactionHandler {
100112
)));
101113
}
102114

103-
// Check that the agent has the pike permission "can_create_product" for the organization
104-
check_permission(perm_checker, signer, "can_create_product")?;
115+
// Check if product exists in state
116+
if state.get_product(product_id)?.is_some() {
117+
return Err(ApplyError::InvalidTransaction(format!(
118+
"Product already exists: {}",
119+
product_id,
120+
)));
121+
}
105122

106123
// Check if the product type is a GS1 product
107-
if payload.get_product_type() != ProductCreateAction_ProductType::GS1 {
124+
if product_type != &ProductType::GS1 {
108125
return Err(ApplyError::InvalidTransaction(
109126
"Invalid product type enum for product".to_string(),
110127
));
111128
}
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;
114129

115130
// Check if product identifier is a valid gtin
116-
let product_id = payload.get_identifier();
117131
if let Err(e) = validate_gtin(product_id) {
118132
return Err(ApplyError::InvalidTransaction(e.to_string()));
119133
}
120134

121135
// Check that the org owns the GS1 company prefix in the identifier
122-
let org = match state.get_organization(payload.get_owner())? {
136+
let org = match state.get_organization(payload.owner())? {
123137
Some(org) => org,
124138
None => {
125139
return Err(ApplyError::InvalidTransaction(format!(
126-
"The Agents organization does not exist: {}",
127-
signer
140+
"The Agent's organization does not exist: {}",
141+
signer,
128142
)));
129143
}
130144
};
131145

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-
144146
/* Check if the agents organization contain GS1 Company Prefix key in its metadata
145147
(gs1_company_prefixes), and the prefix must match the company prefix in the identifier */
146148
let gs1_company_prefix_vec = org.metadata().to_vec();
@@ -167,25 +169,32 @@ impl ProductTransactionHandler {
167169
}
168170
}
169171

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-
));
172+
let new_product = ProductBuilder::new()
173+
.with_identifier(product_id.to_string())
174+
.with_owner(owner.to_string())
175+
.with_product_type(product_type.clone())
176+
.with_properties(properties.to_vec())
177+
.build()
178+
.map_err(|err| {
179+
ApplyError::InvalidTransaction(format!("Cannot build product: {}", err))
180+
})?;
181+
182+
state.set_product(product_id, new_product)?;
177183

178-
state.set_product(signer, new_product)?;
179184
Ok(())
180185
}
181186

182187
fn update_product(
183188
&self,
184-
payload: ProductUpdateAction,
185-
mut state: ProductState,
189+
payload: &ProductUpdateAction,
190+
state: &mut ProductState,
186191
signer: &str,
187192
perm_checker: &PermissionChecker,
188193
) -> Result<(), ApplyError> {
194+
let product_id = payload.identifier();
195+
let product_type = payload.product_type();
196+
let properties = payload.properties();
197+
189198
// Check that the agent submitting the transactions exists in state
190199
let agent = match state.get_agent(signer)? {
191200
Some(agent) => agent,
@@ -197,22 +206,26 @@ impl ProductTransactionHandler {
197206
}
198207
};
199208

209+
// Check signing agent's permission
210+
check_permission(perm_checker, signer, "can_update_product")?;
211+
212+
// Check that the agent has an organization associated with it
213+
if agent.org_id().is_empty() {
214+
return Err(ApplyError::InvalidTransaction(format!(
215+
"The signing Agent does not have an associated organization: {}",
216+
signer
217+
)));
218+
}
219+
200220
// Check if the product type is a GS1 product
201-
let product_type = payload.get_product_type();
202-
if product_type != ProductUpdateAction_ProductType::GS1 {
221+
if product_type != &ProductType::GS1 {
203222
return Err(ApplyError::InvalidTransaction(
204223
"Invalid product type enum for product".to_string(),
205224
));
206225
}
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-
}
213226

214227
// Check if product exists
215-
let mut product = match state.get_product(product_id) {
228+
let product = match state.get_product(product_id) {
216229
Ok(Some(product)) => Ok(product),
217230
Ok(None) => Err(ApplyError::InvalidTransaction(format!(
218231
"No product exists: {}",
@@ -222,31 +235,44 @@ impl ProductTransactionHandler {
222235
}?;
223236

224237
// Check if the agent updating the product is part of the organization associated with the product
225-
if product.get_owner() != agent.org_id() {
238+
if product.owner() != agent.org_id() {
226239
return Err(ApplyError::InvalidTransaction(
227240
"Invalid organization for the agent submitting this transaction".to_string(),
228241
));
229242
}
230243

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

234249
// Handle updating the product
235-
let updated_product_values = payload.properties.clone();
236-
product.set_product_values(updated_product_values);
250+
let updated_product = ProductBuilder::new()
251+
.with_identifier(product_id.to_string())
252+
.with_owner(product.owner().to_string())
253+
.with_product_type(product_type.clone())
254+
.with_properties(properties.to_vec())
255+
.build()
256+
.map_err(|err| {
257+
ApplyError::InvalidTransaction(format!("Cannot build product: {}", err))
258+
})?;
259+
260+
state.set_product(product_id, updated_product)?;
237261

238-
state.set_product(product_id, product)?;
239262
Ok(())
240263
}
241264

242265
fn delete_product(
243266
&self,
244-
payload: ProductDeleteAction,
245-
mut state: ProductState,
267+
payload: &ProductDeleteAction,
268+
state: &mut ProductState,
246269
signer: &str,
247270
perm_checker: &PermissionChecker,
248271
) -> Result<(), ApplyError> {
249-
// Check that the agent (signer) submitting the transactions exists in state
272+
let product_id = payload.identifier();
273+
let product_type = payload.product_type();
274+
275+
// Check that the agent submitting the transactions exists in state
250276
let agent = match state.get_agent(signer)? {
251277
Some(agent) => agent,
252278
None => {
@@ -257,19 +283,15 @@ impl ProductTransactionHandler {
257283
}
258284
};
259285

286+
// Check signing agent's permission
287+
check_permission(perm_checker, signer, "can_delete_product")?;
288+
260289
// Check if the product type is a GS1 product
261-
let product_type = payload.get_product_type();
262-
if product_type != ProductDeleteAction_ProductType::GS1 {
290+
if product_type != &ProductType::GS1 {
263291
return Err(ApplyError::InvalidTransaction(
264292
"Invalid product type enum for product".to_string(),
265293
));
266294
}
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-
}
273295

274296
// Check if product exists in state
275297
let product = match state.get_product(product_id) {
@@ -281,16 +303,18 @@ impl ProductTransactionHandler {
281303
Err(err) => Err(err),
282304
}?;
283305

306+
// Check if product identifier is a valid gtin
307+
if let Err(e) = validate_gtin(product_id) {
308+
return Err(ApplyError::InvalidTransaction(e.to_string()));
309+
}
310+
284311
// 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() {
312+
if product.owner() != agent.org_id() {
286313
return Err(ApplyError::InvalidTransaction(
287314
"Invalid organization for the agent submitting this transaction".to_string(),
288315
));
289316
}
290317

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-
294318
// Delete the product
295319
state.remove_product(product_id)?;
296320
Ok(())
@@ -315,45 +339,31 @@ impl TransactionHandler for ProductTransactionHandler {
315339
request: &TpProcessRequest,
316340
context: &mut dyn TransactionContext,
317341
) -> 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-
};
342+
let payload = ProductPayload::from_bytes(request.get_payload()).map_err(|err| {
343+
ApplyError::InvalidTransaction(format!("Cannot build product payload: {}", err))
344+
})?;
331345

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

338348
info!(
339349
"Grid Product Payload {:?} {}",
340-
payload.get_action(),
341-
payload.get_timestamp()
350+
payload.action(),
351+
payload.timestamp(),
342352
);
343353

344354
let signer = request.get_header().get_signer_public_key();
345-
let state = ProductState::new(context);
355+
let mut state = ProductState::new(context);
346356
let perm_checker = PermissionChecker::new(context);
347357

348-
match payload.get_action() {
349-
Action::CreateProduct(create_product_payload) => {
350-
self.create_product(create_product_payload, state, signer, &perm_checker)?
358+
match payload.action() {
359+
Action::ProductCreate(create_product_payload) => {
360+
self.create_product(create_product_payload, &mut state, signer, &perm_checker)?
351361
}
352-
Action::UpdateProduct(update_product_payload) => {
353-
self.update_product(update_product_payload, state, signer, &perm_checker)?
362+
Action::ProductUpdate(update_product_payload) => {
363+
self.update_product(update_product_payload, &mut state, signer, &perm_checker)?
354364
}
355-
Action::DeleteProduct(delete_product_payload) => {
356-
self.delete_product(delete_product_payload, state, signer, &perm_checker)?
365+
Action::ProductDelete(delete_product_payload) => {
366+
self.delete_product(delete_product_payload, &mut state, signer, &perm_checker)?
357367
}
358368
}
359369
Ok(())

0 commit comments

Comments
 (0)