4 releases

0.0.4 Nov 1, 2021
0.0.3 Oct 25, 2021
0.0.2 Oct 25, 2021
0.0.1 Oct 23, 2021

#1769 in Database interfaces

MIT license

47KB
1K SLoC

Klub models

Rust library crate for sharing model traits, structs and interfaces.

Models are used to describe entities and relationships. Each model has a unique schema and is a mod in this crate.

The advantages of making each model a mod in a centralized lib are:

  • Version control models
  • Can be shared across numerous rust repos
  • GraphQL schemas are generated using the structs and traits defined in this lib
  • Front-end repos can also use this lib to generate typescript types if built for wasm targets
  • Code re-usability and less maintainance

Installation

The crate has been published and it publicly available. This not ideal. We need to make the crate private eventually before going to production.

View the latest version here

Development

  1. Clone klub-com/klub-core
  2. Run make build
  3. To sym link this package with other rust apps, add rust klub_models = { path = "../../core/models", version = "0.0.3" } to your Cargo.toml

You should now be able to make changes and use your IDE to run tests.

Publishing

  1. Manually increment the verision in core/models/Cargo.toml
  2. Go to root directory of repo and run make release-models

View crate docs

  1. Run cargo install https - to run cargo doc on localhost:3000
  2. Run npm install -g browser-sync - to live reload your docs as you make changes
  3. Run make docs in the root directory of the repo to start the rust docs server
  4. Navigate to localhost:3000/klub_models to view docs

Usage


Easily construct a model

let problem = Problem::new(id, name);

Up cast models to their super class.

Since Problem implements the Topic trait. You can use .into() to up-cast Problem to Topic.

let problem = Problem::new(id, name);
let topic = Some(problem.clone().into()); // topic is problem's super class

Match against them.

Every model mode with sub classes declares an enum deriving strum crate macros. Strum makes it easy to cast strings to enums and vice versa. This make is easy to match strings against emums.

let model_name_as_enum = TopicType::from_str(&model_name).unwrap();
match model_name_as_enum {
    TopicType::Problem => {
        let problem = Problem::new(id_str, name_str, &["2000"], &["8000"]);
        return Some(problem.clone().into());
    }
    TopicType::Discussion => {
        let discussion = Discussion::new(id_str, name_str, &["2000"], &["8000"]);
        return Some(discussion.clone().into());
    }
}

Now let's put it all together.

In this example we are getting a topic from Neo4J graph database. We then serialize the query response neo4rs::Node to a GraphQL TopicValue interface manually. We then cast the Neo4J node's model_name to a TopicType enum. We then match against the TopicTypes and construct either a Problem or a Discussion. Lastly we up cast the Problem/Discussion to being a TopicValue since that is what the GraphQL field is expecting as the return type.

use klub_models::topic::topic::{TopicType, TopicValue, TOPIC_MODEL_ALIAS, TOPIC_MODEL_NAME};
...

// Example GraphQL topic field resolver
async fn topic(
 #[graphql(context)] ctx: &GraphContext,
 #[graphql(description = "id of topic")] id: String,
 ) -> Option<TopicValue> {
 let model_params = ModelParams {
  model_name: TOPIC_MODEL_NAME,
  model_alias: TOPIC_MODEL_ALIAS,
};
let id_str = &id[..];

// Query neo4j for a topic by id
let topic_node = &ctx.find_one_by_id(model_params, id_str).await.unwrap();

// Convert Neo4J fields to GraphQL fields
let name: String = topic_node.get("name").unwrap();
let name_str = &name[..];
let model_name: String = topic_node.get("model_name").unwrap();
let model_name_as_enum = TopicType::from_str(&model_name).unwrap();

// Cast generic topic to either a Problem or Discussion
match model_name_as_enum {
 TopicType::Problem => {
      let problem = Problem::new(id_str, name_str, &["2000"], &["8000"]);

      // Cast back to TopicValue
      return Some(problem.clone().into());
  }
  TopicType::Discussion => {
      let discussion = Discussion::new(id_str, name_str, &["2000"], &["8000"]);

      // Cast back to TopicValue
      return Some(discussion.clone().into());
  }
 }
}

Dependencies

~24–57MB
~1M SLoC