Skip to content

Commit

Permalink
feat: Initial implementation of products
Browse files Browse the repository at this point in the history
  • Loading branch information
dejanb committed Jun 6, 2024
1 parent 88ec8da commit 2573b01
Show file tree
Hide file tree
Showing 18 changed files with 708 additions and 1 deletion.
30 changes: 30 additions & 0 deletions docs/design/products.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
```mermaid
---
title: Product Structure
---
erDiagram
Organization {
int32 id
string name
}
Product {
int32 id
string name
}
ProductVersion {
int32 id
string version
}
Sbom {
uuid id
}
Organization || -- o{ Product : produces
Product || -- o{ ProductVersion : have
ProductVersion || -- || Sbom : describes
```
2 changes: 2 additions & 0 deletions entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub mod package;
pub mod package_relates_to_package;
pub mod package_version;
pub mod package_version_range;
pub mod product;
pub mod product_version;
pub mod qualified_package;
pub mod relationship;
pub mod sbom;
Expand Down
8 changes: 7 additions & 1 deletion entity/src/organization.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::advisory;
use crate::{advisory, product};
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
Expand All @@ -20,4 +20,10 @@ impl Related<advisory::Entity> for Entity {
}
}

impl Related<product::Entity> for Entity {
fn to() -> RelationDef {
super::product::Relation::Vendor.def().rev()
}
}

impl ActiveModelBehavior for ActiveModel {}
37 changes: 37 additions & 0 deletions entity/src/product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use sea_orm::entity::prelude::*;

use crate::organization;

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "product")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub vendor_id: Option<i32>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::organization::Entity"
from = "Column::VendorId"
to = "super::organization::Column::Id")]
Vendor,
#[sea_orm(has_many = "super::product_version::Entity")]
ProductVersion,
}

impl Related<organization::Entity> for Entity {
fn to() -> RelationDef {
Relation::Vendor.def()
}
}

impl Related<super::product_version::Entity> for Entity {
fn to() -> RelationDef {
Relation::ProductVersion.def()
}
}

impl ActiveModelBehavior for ActiveModel {}
28 changes: 28 additions & 0 deletions entity/src/product_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "product_version")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub product_id: i32,
pub sbom_id: Option<Uuid>,
pub version: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::product::Entity",
from = "super::product_version::Column::ProductId"
to = "super::product::Column::Id")]
Product,
}

impl Related<super::product::Entity> for Entity {
fn to() -> RelationDef {
Relation::Product.def()
}
}

impl ActiveModelBehavior for ActiveModel {}
4 changes: 4 additions & 0 deletions migration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ mod m0000250_create_sbom_package;
mod m0000260_sbom_package_cpe_ref;
mod m0000270_sbom_package_purl_ref;
mod m0000280_add_advisory_vulnerability_meta;
mod m0000290_create_product;
mod m0000300_create_product_version;

pub struct Migrator;

Expand Down Expand Up @@ -60,6 +62,8 @@ impl MigratorTrait for Migrator {
Box::new(m0000260_sbom_package_cpe_ref::Migration),
Box::new(m0000270_sbom_package_purl_ref::Migration),
Box::new(m0000280_add_advisory_vulnerability_meta::Migration),
Box::new(m0000290_create_product::Migration),
Box::new(m0000300_create_product_version::Migration),
]
}
}
Expand Down
50 changes: 50 additions & 0 deletions migration/src/m0000290_create_product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use sea_orm_migration::prelude::*;

use crate::m0000022_create_organization::Organization;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Replace the sample below with your own migration scripts
manager
.create_table(
Table::create()
.table(Product::Table)
.col(
ColumnDef::new(Product::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(Product::Name).string().not_null())
.col(ColumnDef::new(Product::VendorId).integer() /* allowed to be null if not known */)
.foreign_key(
ForeignKey::create()
.from_col(Product::VendorId)
.to(Organization::Table, Organization::Id)
)
.to_owned(),
)
.await?;

Ok(())
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Product::Table).to_owned())
.await
}
}

#[derive(DeriveIden)]
pub enum Product {
Table,
Id,
Name,
VendorId,
}
97 changes: 97 additions & 0 deletions migration/src/m0000300_create_product_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use sea_orm_migration::prelude::*;

use crate::{m0000030_create_sbom::Sbom, m0000290_create_product::Product, Now};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Replace the sample below with your own migration scripts
manager
.create_table(
Table::create()
.table(ProductVersion::Table)
.if_not_exists()
.col(
ColumnDef::new(ProductVersion::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(
ColumnDef::new(ProductVersion::Timestamp)
.timestamp_with_time_zone()
.default(Func::cust(Now)),
)
.col(
ColumnDef::new(ProductVersion::ProductId)
.integer()
.not_null(),
)
.col(ColumnDef::new(ProductVersion::SbomId).uuid())
.col(ColumnDef::new(ProductVersion::Version).string().not_null())
.foreign_key(
ForeignKey::create()
.from_col(ProductVersion::ProductId)
.to(Product::Table, Product::Id)
.on_delete(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.from_col(ProductVersion::SbomId)
.to(Sbom::Table, Sbom::SbomId)
.on_delete(ForeignKeyAction::SetNull),
)
.to_owned(),
)
.await?;

manager
.create_index(
Index::create()
.table(ProductVersion::Table)
.name(INDEX_BY_PID_V)
.if_not_exists()
.unique()
.col(ProductVersion::ProductId)
.col(ProductVersion::Version)
.to_owned(),
)
.await?;

Ok(())
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_index(
Index::drop()
.table(ProductVersion::Table)
.name(INDEX_BY_PID_V)
.if_exists()
.to_owned(),
)
.await?;
manager
.drop_table(Table::drop().table(ProductVersion::Table).to_owned())
.await?;

Ok(())
}
}

const INDEX_BY_PID_V: &str = "by_productid_v";

#[derive(DeriveIden)]
pub enum ProductVersion {
Table,
Id,
Timestamp,
// --
ProductId,
SbomId,
Version,
}
1 change: 1 addition & 0 deletions modules/fundamental/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod advisory;
pub mod organization;

pub mod package;
pub mod product;
pub mod sbom;
pub mod vulnerability;

Expand Down
2 changes: 2 additions & 0 deletions modules/fundamental/src/product/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod model;
pub mod service;
27 changes: 27 additions & 0 deletions modules/fundamental/src/product/model/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

mod summary;
pub use summary::*;

use crate::Error;
use trustify_common::db::ConnectionOrTransaction;
use trustify_entity::product;

#[derive(Serialize, Deserialize, Debug, Clone, ToSchema)]
pub struct ProductHead {
pub id: i32,
pub name: String,
}

impl ProductHead {
pub async fn from_entity(
product: &product::Model,
_tx: &ConnectionOrTransaction<'_>,
) -> Result<Self, Error> {
Ok(ProductHead {
id: product.id,
name: product.name.clone(),
})
}
}
41 changes: 41 additions & 0 deletions modules/fundamental/src/product/model/summary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::product::model::ProductHead;
use crate::Error;
use serde::{Deserialize, Serialize};
use trustify_common::db::ConnectionOrTransaction;
use trustify_common::paginated;
use trustify_entity::product;
use utoipa::ToSchema;

#[derive(Serialize, Deserialize, Clone, Debug, ToSchema)]
pub struct ProductSummary {
#[serde(flatten)]
pub head: ProductHead,
}

paginated!(ProductSummary);

impl ProductSummary {
pub async fn from_entity(
product: &product::Model,
tx: &ConnectionOrTransaction<'_>,
) -> Result<Self, Error> {
Ok(ProductSummary {
head: ProductHead::from_entity(product, tx).await?,
})
}

pub async fn from_entities(
products: &[product::Model],
tx: &ConnectionOrTransaction<'_>,
) -> Result<Vec<Self>, Error> {
let mut summaries = Vec::new();

for org in products {
summaries.push(ProductSummary {
head: ProductHead::from_entity(org, tx).await?,
});
}

Ok(summaries)
}
}
Loading

0 comments on commit 2573b01

Please sign in to comment.