ディーゼル vie mysql でグラフの 1 対多のリレーショナル クエリを作成しようとすると、enum のクエリが fromIterator で失敗します。`std::iter::FromIterator<_> を実装する必要があることを示すのに役立ちますが、構造体を正確に実装する方法がわかりません。何か役に立ちますか?前もって感謝します!
エラーログ
error[E0277]: a value of type `&std::vec::Vec<graphql::ContactContent>` cannot be built from an iterator over elements of type `_`
--> src/graphql.rs:129:89
|
129 | .and_then(|contact_contents| Ok(contact_contents.into_iter().map_into().collect()))
| ^^^^^^^ value of type `&std::vec::Vec<graphql::ContactContent>` cannot be built from `std::iter::Iterator<Item=_>`
|
= help: the trait `std::iter::FromIterator<_>` is not implemented for `&std::vec::Vec<graphql::ContactContent>`
cargo.toml
[package]
name = "lead_generation_api"
version = "0.1.0"
authors = ["ARBOR.JP"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
diesel = { version = "1.4.5", features = ["mysql", "r2d2", "chrono"] }
dotenv = "~0.15"
serde = "~1.0"
serde_derive = "~1.0"
serde_json = "~1.0"
chrono = "~0.4"
rand = "0.7.3"
actix-web = "1.0.9"
actix-cors = "0.1.0"
juniper = "0.14.1"
juniper-from-schema = "0.5.1"
juniper-eager-loading = "0.5.0"
r2d2_mysql = "*"
r2d2-diesel = "0.16.0"
mysql = "*"
r2d2 = "*"
futures01 = "0.1.29"
itertools = "0.8.2"
[[bin]]
name = "main"
path = "src/main.rs"
src/graphql.rs
use std::convert::From;
use std::sync::Arc;
use chrono::NaiveDateTime;
use actix_web::{web, Error, HttpResponse};
use futures01::future::Future;
use juniper::http::playground::playground_source;
use juniper::{http::GraphQLRequest,GraphQLObject, Executor, FieldResult, FieldError,ID};
use juniper_from_schema::graphql_schema_from_file;
use diesel::prelude::*;
use itertools::Itertools;
use crate::schema::{customers, contact_contents};
use crate::{DbCon, DbPool};
graphql_schema_from_file!("src/schema.graphql");
pub struct Context {
db_con: DbCon,
}
impl juniper::Context for Context {}
pub struct Query;
pub struct Mutation;
impl QueryFields for Query {
fn field_customers(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Customer, Walked>,
) -> FieldResult<Vec<Customer>> {
//type FieldResult<T> = Result<T, String>;
customers::table
.load::<crate::models::Customer>(&executor.context().db_con)
.and_then(|customers| Ok(customers.into_iter().map_into().collect()))
.map_err(Into::into)
}
}
impl MutationFields for Mutation {
fn field_create_customer(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Customer, Walked>,
name: String,
email: String,
contact_contents: Vec<String>,
) -> FieldResult<Customer> {
//type FieldResult<T> = Result<T, String>;
let new_customer = crate::models::NewCustomer { name: name, email: email};
diesel::insert_into(customers::table)
.values(&new_customer)
.execute(&executor.context().db_con);
customers::table
.first::<crate::graphql::Customer>(&executor.context().db_con)
.map_err(Into::into)
}
}
//#[derive(GraphQLObject)]
//#[graphql_object(name="customer name", email ="mail address")]
#[derive(Queryable)]
pub struct Customer {
id: u64,
name: String,
email: String,
created_at: NaiveDateTime,
updated_at: NaiveDateTime,
}
#[derive(Queryable)]
pub struct ContactContent {
id: u64,
customer_id: u64,
email: String,
}
impl ContactContentFields for ContactContent {
fn field_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
Ok(juniper::ID::new(self.id.to_string()))
}
fn field_customer_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
Ok(juniper::ID::new(self.customer_id.to_string()))
}
fn field_email(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
Ok(&self.email)
}
}
impl From<crate::models::ContactContent> for ContactContent {
fn from(contact_contents: crate::models::ContactContent) -> Self {
Self {
id: contact_contents.id,
customer_id: contact_contents.customer_id,
email: contact_contents.email,
}
}
}
//#[juniper::object(Context = Context)]
//#[graphql(description = "A Photo returns struct")]
impl CustomerFields for Customer {
fn field_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
Ok(juniper::ID::new(self.id.to_string()))
}
fn field_name(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
Ok(&self.name)
}
fn field_email(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
Ok(&self.email)
}
fn field_contact_contents(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, ContactContent, Walked>,
) -> FieldResult<&Vec<ContactContent>> {
contact_contents::table
.filter(contact_contents::customer_id.eq(&self.id))
.load::<crate::models::ContactContent>(&executor.context().db_con)
.and_then(|contact_contents| Ok(contact_contents.into_iter().map_into().collect()))
.map_err(Into::into)
}
}
impl From<crate::models::Customer> for Customer {
fn from(customer: crate::models::Customer) -> Self {
Self {
id: customer.id,
name: customer.name,
email: customer.email,
created_at: customer.created_at,
updated_at: customer.updated_at,
}
}
}
fn playground() -> HttpResponse {
let html = playground_source("");
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html)
}
fn graphql(
schema: web::Data<Arc<Schema>>,
data: web::Json<GraphQLRequest>,
db_pool: web::Data<DbPool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let ctx = Context {
db_con: db_pool.get().unwrap(),
};
web::block(move || {
let res = data.execute(&schema, &ctx);
Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
})
.map_err(Error::from)
.and_then(|customer| {
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(customer))
})
}
pub fn register(config: &mut web::ServiceConfig) {
let schema = std::sync::Arc::new(Schema::new(Query, Mutation));
config
.data(schema)
.route("/", web::post().to_async(graphql))
.route("/", web::get().to(playground));
}
src/schema.rs
table! {
contact_contents (id) {
id -> Unsigned<Bigint>,
customer_id -> Unsigned<Bigint>,
email -> Varchar,
}
}
table! {
customers (id) {
id -> Unsigned<Bigint>,
name -> Varchar,
email -> Varchar,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}
src/schema.graphql
schema {
query: Query
mutation: Mutation
}
type Query {
customers: [Customer!]! @juniper(ownership: "owned")
}
type Mutation {
createCustomer(
name: String!
email: String!
contactContents: [String!]!
): Customer! @juniper(ownership: "owned")
}
type Customer {
id: ID! @juniper(ownership: "owned")
name: String!
email: String!
contactContents: [ContactContent!]!
}
type ContactContent {
id: ID! @juniper(ownership: "owned")
customerId: ID! @juniper(ownership: "owned")
email: String!
}
src/models.rs
use chrono::NaiveDateTime;
pub struct Query;
pub struct Mutation;
#[derive(Queryable, Identifiable, AsChangeset, Clone, PartialEq, Debug)]
pub struct Customer {
pub id: u64,
pub name: String,
pub email: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
use super::schema::customers;
#[derive(Queryable,Insertable, AsChangeset)]
#[table_name="customers"]
pub struct NewCustomer {
pub name: String,
pub email: String,
}
use super::schema::contact_contents;
#[derive(Queryable,Identifiable)]
#[table_name="contact_contents"]
pub struct ContactContent {
pub id: u64,
pub customer_id: u64,
pub email: String,
}
src/main.rs
#[macro_use]
extern crate diesel;
extern crate r2d2;
use actix_cors::Cors;
use actix_web::{web, App, HttpServer};
use diesel::{
prelude::*,
};
use diesel::r2d2::ConnectionManager;
pub mod graphql;
pub mod models;
pub mod schema;
pub type DbPool = r2d2::Pool<ConnectionManager<MysqlConnection>>;
pub type DbCon = r2d2::PooledConnection<ConnectionManager<MysqlConnection>>;
fn main() {
let db_pool = create_db_pool();
let port: u16 = std::env::var("PORT")
.ok()
.and_then(|p| p.parse().ok())
.unwrap_or(8080);
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
HttpServer::new(move || {
App::new()
.data(db_pool.clone())
.wrap(Cors::new())
.configure(graphql::register)
.default_service(web::to(|| "404"))
})
.bind(addr)
.unwrap()
.run()
.unwrap();
}
pub fn create_db_pool() -> DbPool {
let manager = ConnectionManager::<MysqlConnection>::new("mysql://root:example@mysql/test");
r2d2::Pool::new(manager).expect("Failed to create DB Pool")
}