Skip to content

Commit

Permalink
Reapply "tsparser: improved app validation" (encoredev#1633)
Browse files Browse the repository at this point in the history
This reverts commit 7aca2b4.
  • Loading branch information
eandre committed Dec 10, 2024
1 parent cb5c6d9 commit c85ad0d
Show file tree
Hide file tree
Showing 15 changed files with 1,385 additions and 341 deletions.
1 change: 1 addition & 0 deletions runtimes/go/storage/sqldb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func (db *Database) Begin(ctx context.Context) (*Tx, error) {
// this will be made with backwards compatibility in mind, providing ample notice and
// time to migrate in an opt-in fashion.
func Driver[T SupportedDrivers](db *Database) T {
db.init()
if db.noopDB {
var zero T
return zero
Expand Down
98 changes: 88 additions & 10 deletions tsparser/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ use std::collections::HashMap;

use matchit::InsertError;
use swc_common::errors::HANDLER;
use swc_common::Span;

use crate::encore::parser::meta::v1;
use crate::legacymeta::compute_meta;
use crate::parser::parser::{ParseContext, ParseResult};
use crate::parser::resources::apis::api::{Method, Methods};
use crate::parser::resources::apis::api::{Endpoint, Method, Methods};
use crate::parser::resources::Resource;
use crate::parser::respath::Path;
use crate::parser::types::visitor::VisitWith;
use crate::parser::types::{validation, visitor, ResolveState, Type, Validated};
use crate::parser::Range;
use litparser::ParseResult as PResult;
use crate::span_err::ErrReporter;
use litparser::Sp;

#[derive(Debug)]
pub struct AppDesc {
Expand Down Expand Up @@ -60,9 +64,31 @@ impl Router {
}
}

impl AppDesc {
pub fn validate_and_describe(pc: &ParseContext, parse: ParseResult) -> Option<AppDesc> {
AppValidator { pc, parse: &parse }.validate();

if pc.errs.has_errors() {
return None;
}

match compute_meta(pc, &parse) {
Ok(meta) => Some(AppDesc { parse, meta }),
Err(err) => {
err.report();
None
}
}
}

struct AppValidator<'a> {
pc: &'a ParseContext,
parse: &'a ParseResult,
}

impl AppValidator<'_> {
fn validate(&self) {
self.validate_apis()
self.validate_apis();
self.validate_pubsub()
}

fn validate_apis(&self) {
Expand All @@ -72,17 +98,69 @@ impl AppDesc {
if let Resource::APIEndpoint(endpoint) = &bind.resource {
let encoding = &endpoint.encoding;
router.try_add(&encoding.methods, &encoding.path, endpoint.range);

self.validate_endpoint(endpoint);
}
}
}
}
}

pub fn validate_and_describe(pc: &ParseContext, parse: ParseResult) -> PResult<AppDesc> {
let meta = compute_meta(pc, &parse)?;
let desc = AppDesc { parse, meta };
fn validate_endpoint(&self, ep: &Endpoint) {
if let Some(schema) = &ep.encoding.raw_req_schema {
self.validate_validations(schema);
}
if let Some(schema) = &ep.encoding.raw_resp_schema {
self.validate_validations(schema);
}
}

desc.validate();
fn validate_validations(&self, schema: &Sp<Type>) {
struct Visitor<'a> {
state: &'a ResolveState,
span: Span,
}

Ok(desc)
impl visitor::Visit for Visitor<'_> {
fn resolve_state(&self) -> &ResolveState {
self.state
}

fn visit_validated(&mut self, node: &Validated) {
if let Err(err) = node.expr.supports_type(&node.typ) {
let s = err.to_string();
self.span.err(&s);
} else {
// Don't recurse into the validation expression, as it would report an error
// below as if the expression was standalone.
node.typ.visit_with(self);
}
}

fn visit_validation(&mut self, node: &validation::Expr) {
HANDLER.with(|h| {
h.struct_span_err(
self.span,
&format!("unsupported standalone validation expression: {}", node),
)
.note("validation expressions must be attached to a regular type using '&'")
.emit()
});
}
}

let state = self.pc.type_checker.state();
let mut visitor = Visitor {
state,
span: schema.span(),
};
schema.visit_with(&mut visitor);
}

fn validate_pubsub(&self) {
for res in self.parse.resources.iter() {
if let Resource::PubSubTopic(topic) = &res {
self.validate_validations(&topic.message_type);
}
}
}
}
8 changes: 1 addition & 7 deletions tsparser/src/builder/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ impl Builder<'_> {
let parser = Parser::new(pc, pass1);

let result = parser.parse();
let desc = match validate_and_describe(pc, result) {
Ok(desc) => desc,
Err(err) => {
err.report();
return None;
}
};
let desc = validate_and_describe(pc, result)?;

if pc.errs.has_errors() {
None
Expand Down
30 changes: 15 additions & 15 deletions tsparser/src/legacymeta/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ use itertools::Itertools;
use litparser::{ParseResult, ToParseErr};
use swc_common::errors::HANDLER;

use crate::encore::parser::schema::v1 as schema;
use crate::encore::parser::schema::v1::r#type as styp;
use crate::encore::parser::schema::v1::{self as schema};
use crate::legacymeta::api_schema::strip_path_params;
use crate::parser::parser::ParseContext;

use crate::parser::resources::apis::api::Endpoint;
use crate::parser::types::custom::{resolve_custom_type_named, CustomType};
use crate::parser::types::{
drop_empty_or_void, Basic, EnumValue, FieldName, Generic, Interface, Literal, Named, ObjectId,
Type,
Type, Union,
};
use crate::parser::{FilePath, FileSet, Range};

Expand Down Expand Up @@ -87,7 +87,7 @@ impl BuilderCtx<'_, '_> {
Ok(match typ {
Type::Basic(tt) => self.basic(tt),
Type::Array(tt) => {
let elem = self.typ(tt)?;
let elem = self.typ(&tt.0)?;
schema::Type {
typ: Some(styp::Typ::List(Box::new(schema::List {
elem: Some(Box::new(elem)),
Expand Down Expand Up @@ -118,9 +118,9 @@ impl BuilderCtx<'_, '_> {
validation: None,
},

Type::Union(types) => schema::Type {
Type::Union(union) => schema::Type {
typ: Some(styp::Typ::Union(schema::Union {
types: self.types(types)?,
types: self.types(&union.types)?,
})),
validation: None,
},
Expand Down Expand Up @@ -153,7 +153,7 @@ impl BuilderCtx<'_, '_> {
}
}
Type::Optional(_) => anyhow::bail!("optional types are not yet supported in schemas"),
Type::This => anyhow::bail!("this types are not yet supported in schemas"),
Type::This(_) => anyhow::bail!("this types are not yet supported in schemas"),
Type::Generic(typ) => match typ {
Generic::TypeParam(param) => {
let decl_id = self
Expand Down Expand Up @@ -183,10 +183,10 @@ impl BuilderCtx<'_, '_> {
)
}

Type::Validated((typ, expr)) => {
let mut typ = self.typ(typ)?;
Type::Validated(validated) => {
let mut typ = self.typ(&validated.typ)?;
// Simplify the validation expression, if possible.
let expr = expr.clone().simplify();
let expr = validated.expr.clone().simplify();
typ.validation = Some(expr.to_pb());
typ
}
Expand Down Expand Up @@ -539,16 +539,16 @@ impl BuilderCtx<'_, '_> {
/// union and `true` to indicate the type included "| undefined".
/// Otherwise, returns the original type and `false`.
fn drop_undefined_union(typ: &Type) -> (Cow<'_, Type>, bool) {
if let Type::Union(types) = &typ {
for (i, t) in types.iter().enumerate() {
if let Type::Union(union) = &typ {
for (i, t) in union.types.iter().enumerate() {
if let Type::Basic(Basic::Undefined) = &t {
// If we have a union with only two types, return the other type.
return if types.len() == 2 {
(Cow::Borrowed(&types[1 - i]), true)
return if union.types.len() == 2 {
(Cow::Borrowed(&union.types[1 - i]), true)
} else {
let mut types = types.clone();
let mut types = union.types.clone();
types.swap_remove(i);
(Cow::Owned(Type::Union(types)), true)
(Cow::Owned(Type::Union(Union { types })), true)
};
}
}
Expand Down
7 changes: 3 additions & 4 deletions tsparser/src/parser/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ pub mod custom;
mod object;
mod typ;
mod type_resolve;
mod type_string;
mod utils;
pub mod visitor;

mod resolved;
#[cfg(test)]
mod tests;
pub mod validation;

pub use object::{Object, ObjectId, ObjectKind, ResolveState};
pub use typ::{
Basic, ClassType, EnumMember, EnumType, EnumValue, FieldName, Generic, Interface,
InterfaceField, Literal, Named, Type, TypeArgId,
};
pub use typ::*;
pub use type_resolve::TypeChecker;
pub use utils::*;
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,20 @@ input_file: tsparser/src/parser/types/testdata/basic.ts
},
),
"Exclude1": Union(
[
Literal(
String(
"bar",
Union {
types: [
Literal(
String(
"bar",
),
),
),
Literal(
String(
"optional",
Literal(
String(
"optional",
),
),
),
],
],
},
),
"Pick1": Interface(
Interface {
Expand Down Expand Up @@ -183,14 +185,16 @@ input_file: tsparser/src/parser/types/testdata/basic.ts
String,
),
Union(
[
Basic(
Boolean,
),
Basic(
Number,
),
],
Union {
types: [
Basic(
Boolean,
),
Basic(
Number,
),
],
},
),
),
),
Expand Down Expand Up @@ -356,37 +360,39 @@ input_file: tsparser/src/parser/types/testdata/basic.ts
},
),
"EnumFields": Union(
[
Literal(
String(
"A",
Union {
types: [
Literal(
String(
"A",
),
),
),
Literal(
String(
"B",
Literal(
String(
"B",
),
),
),
Literal(
String(
"C",
Literal(
String(
"C",
),
),
),
Literal(
String(
"D",
Literal(
String(
"D",
),
),
),
Literal(
String(
"E",
Literal(
String(
"E",
),
),
),
Literal(
String(
"F",
Literal(
String(
"F",
),
),
),
],
],
},
),
}
Loading

0 comments on commit c85ad0d

Please sign in to comment.