Skip to content

Commit e5ed6d0

Browse files
Aaron Elinejohn-h-kastner-aws
authored andcommitted
Adding read/write json methods to entity (#924)
Signed-off-by: Aaron Eline <[email protected]> Co-authored-by: John Kastner <[email protected]>
1 parent 96f4fde commit e5ed6d0

File tree

5 files changed

+594
-1
lines changed

5 files changed

+594
-1
lines changed

cedar-policy-core/src/ast/entity.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
use crate::ast::*;
18+
use crate::entities::{EntitiesError, EntityJson, JsonSerializationError};
1819
use crate::evaluator::{EvaluationError, RestrictedEvaluator};
1920
use crate::extensions::Extensions;
2021
use crate::parser::err::ParseErrors;
@@ -438,6 +439,27 @@ impl Entity {
438439
ancestors,
439440
)
440441
}
442+
443+
/// Write the entity to a json document
444+
pub fn write_to_json(&self, f: impl std::io::Write) -> Result<(), EntitiesError> {
445+
let ejson = EntityJson::from_entity(self)?;
446+
serde_json::to_writer_pretty(f, &ejson).map_err(JsonSerializationError::from)?;
447+
Ok(())
448+
}
449+
450+
/// write the entity to a json value
451+
pub fn to_json_value(&self) -> Result<serde_json::Value, EntitiesError> {
452+
let ejson = EntityJson::from_entity(self)?;
453+
let v = serde_json::to_value(ejson).map_err(JsonSerializationError::from)?;
454+
Ok(v)
455+
}
456+
457+
/// write the entity to a json string
458+
pub fn to_json_string(&self) -> Result<String, EntitiesError> {
459+
let ejson = EntityJson::from_entity(self)?;
460+
let string = serde_json::to_string(&ejson).map_err(JsonSerializationError::from)?;
461+
Ok(string)
462+
}
441463
}
442464

443465
impl PartialEq for Entity {

cedar-policy-core/src/entities/json/entities.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use super::{
2222
use crate::ast::{
2323
BorrowedRestrictedExpr, Entity, EntityType, EntityUID, PartialValue, RestrictedExpr,
2424
};
25+
use crate::entities::conformance::EntitySchemaConformanceChecker;
2526
use crate::entities::{
2627
schematype_of_partialvalue, Entities, EntitiesError, EntitySchemaConformanceError,
2728
GetSchemaTypeError, TCComputation, UnexpectedEntityTypeError,
@@ -31,8 +32,8 @@ use crate::jsonvalue::JsonValueWithNoDuplicateKeys;
3132
use serde::{Deserialize, Serialize};
3233
use serde_with::serde_as;
3334
use smol_str::SmolStr;
34-
use std::collections::HashMap;
3535
use std::sync::Arc;
36+
use std::{collections::HashMap, io::Read};
3637

3738
#[cfg(feature = "wasm")]
3839
extern crate tsify;
@@ -78,6 +79,7 @@ pub struct EntityJsonParser<'e, 's, S: Schema = NoEntitiesSchema> {
7879
}
7980

8081
/// Schema information about a single entity can take one of these forms:
82+
#[derive(Debug)]
8183
enum EntitySchemaInfo<E: EntityTypeDescription> {
8284
/// There is no schema, i.e. we're not doing schema-based parsing
8385
NoSchema,
@@ -212,6 +214,39 @@ impl<'e, 's, S: Schema> EntityJsonParser<'e, 's, S> {
212214
Ok(entities.into_iter())
213215
}
214216

217+
/// Parse a single entity from an in-memory JSON value
218+
pub fn single_from_json_value(
219+
&self,
220+
value: serde_json::Value,
221+
) -> Result<Entity, EntitiesError> {
222+
let ejson = serde_json::from_value(value).map_err(JsonDeserializationError::from)?;
223+
self.single_from_ejson(ejson)
224+
}
225+
226+
/// Parse a single entity from a JSON string
227+
pub fn single_from_json_str(&self, src: impl AsRef<str>) -> Result<Entity, EntitiesError> {
228+
let ejson = serde_json::from_str(src.as_ref()).map_err(JsonDeserializationError::from)?;
229+
self.single_from_ejson(ejson)
230+
}
231+
232+
/// Parse a single entity from a JSON reader
233+
pub fn single_from_json_file(&self, r: impl Read) -> Result<Entity, EntitiesError> {
234+
let ejson = serde_json::from_reader(r).map_err(JsonDeserializationError::from)?;
235+
self.single_from_ejson(ejson)
236+
}
237+
238+
fn single_from_ejson(&self, ejson: EntityJson) -> Result<Entity, EntitiesError> {
239+
let entity = self.parse_ejson(ejson)?;
240+
match self.schema {
241+
None => Ok(entity),
242+
Some(schema) => {
243+
let checker = EntitySchemaConformanceChecker::new(schema, self.extensions);
244+
checker.validate_entity(&entity)?;
245+
Ok(entity)
246+
}
247+
}
248+
}
249+
215250
/// Internal function that creates an [`Entities`] from a stream of [`EntityJson`].
216251
///
217252
/// If the `EntityJsonParser` has a `schema`, this also adds `Action`

cedar-policy-validator/src/coreschema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ impl<'a> entities::Schema for CoreSchema<'a> {
8282
}
8383

8484
/// Struct which carries enough information that it can impl Core's `EntityTypeDescription`
85+
#[derive(Debug)]
8586
pub struct EntityTypeDescription {
8687
/// Core `EntityType` this is describing
8788
core_type: ast::EntityType,

cedar-policy/src/api.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ use serde::{Deserialize, Serialize};
5656
use smol_str::SmolStr;
5757
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
5858
use std::convert::Infallible;
59+
use std::io::Read;
5960
use std::marker::PhantomData;
6061
use std::str::FromStr;
6162
use thiserror::Error;
@@ -269,6 +270,81 @@ impl Entity {
269270
ancestors.into_iter().map(EntityUid).collect(),
270271
)
271272
}
273+
274+
/// Parse an entity from an in-memory JSON value
275+
/// If a schema is provided, it is handled identically to [`Entities::from_json_str`]
276+
pub fn from_json_value(
277+
value: serde_json::Value,
278+
schema: Option<&Schema>,
279+
) -> Result<Self, EntitiesError> {
280+
let schema = schema.map(|s| cedar_policy_validator::CoreSchema::new(&s.0));
281+
let eparser = cedar_policy_core::entities::EntityJsonParser::new(
282+
schema.as_ref(),
283+
Extensions::all_available(),
284+
cedar_policy_core::entities::TCComputation::ComputeNow,
285+
);
286+
eparser.single_from_json_value(value).map(Self)
287+
}
288+
289+
/// Parse an entity from a JSON string
290+
/// If a schema is provided, it is handled identically to [`Entities::from_json_str`]
291+
pub fn from_json_str(
292+
src: impl AsRef<str>,
293+
schema: Option<&Schema>,
294+
) -> Result<Self, EntitiesError> {
295+
let schema = schema.map(|s| cedar_policy_validator::CoreSchema::new(&s.0));
296+
let eparser = cedar_policy_core::entities::EntityJsonParser::new(
297+
schema.as_ref(),
298+
Extensions::all_available(),
299+
cedar_policy_core::entities::TCComputation::ComputeNow,
300+
);
301+
eparser.single_from_json_str(src).map(Self)
302+
}
303+
304+
/// Parse an entity from a JSON reader
305+
/// If a schema is provided, it is handled identically to [`Entities::from_json_str`]
306+
pub fn from_json_file(f: impl Read, schema: Option<&Schema>) -> Result<Self, EntitiesError> {
307+
let schema = schema.map(|s| cedar_policy_validator::CoreSchema::new(&s.0));
308+
let eparser = cedar_policy_core::entities::EntityJsonParser::new(
309+
schema.as_ref(),
310+
Extensions::all_available(),
311+
cedar_policy_core::entities::TCComputation::ComputeNow,
312+
);
313+
eparser.single_from_json_file(f).map(Self)
314+
}
315+
316+
/// Dump an `Entity` object into an entity JSON file.
317+
///
318+
/// The resulting JSON will be suitable for parsing in via
319+
/// `from_json_*`, and will be parse-able even with no [`Schema`].
320+
///
321+
/// To read an `Entity` object from JSON , use
322+
/// [`Self::from_json_file`], [`Self::from_json_value`], or [`Self::from_json_str`].
323+
pub fn write_to_json(&self, f: impl std::io::Write) -> Result<(), EntitiesError> {
324+
self.0.write_to_json(f)
325+
}
326+
327+
/// Dump an `Entity` object into an in-memory JSON object.
328+
///
329+
/// The resulting JSON will be suitable for parsing in via
330+
/// `from_json_*`, and will be parse-able even with no `Schema`.
331+
///
332+
/// To read an `Entity` object from JSON , use
333+
/// [`Self::from_json_file`], [`Self::from_json_value`], or [`Self::from_json_str`].
334+
pub fn to_json_value(&self) -> Result<serde_json::Value, EntitiesError> {
335+
self.0.to_json_value()
336+
}
337+
338+
/// Dump an `Entity` object into a JSON string.
339+
///
340+
/// The resulting JSON will be suitable for parsing in via
341+
/// `from_json_*`, and will be parse-able even with no `Schema`.
342+
///
343+
/// To read an `Entity` object from JSON , use
344+
/// [`Self::from_json_file`], [`Self::from_json_value`], or [`Self::from_json_str`].
345+
pub fn to_json_string(&self) -> Result<String, EntitiesError> {
346+
self.0.to_json_string()
347+
}
272348
}
273349

274350
impl std::fmt::Display for Entity {

0 commit comments

Comments
 (0)