Giter Club home page Giter Club logo

cruddl's Introduction

cruddl

npm version Build Status Package Quality

cruddl - create a cuddly GraphQL API for your database, using the GraphQL SDL to model your schema.

This TypeScript library creates an executable GraphQL schema from a model definition and provides queries and mutations to access a database. Currently, it supports the multi-model database ArangoDB. The concept being inspired by existing projects like prisma and join-monster, cruddl exploits the expressiveness of the Arango Query Language (AQL) to generate one tailored query for each GraphQL request.

Try it online

Features

  • Schema definition using GraphQL types, fields and directives
  • Modelling features like relations, embedded lists and objects
  • Query features include filtering, sorting, cursor-based pagination and arbitrary nesting
  • Schema validation
  • Role-based authorization (field and type-based; static and data-dependent)
  • Pluggable database backends (currently supports ArangoDB and an in-memory implementation)

Usage

npm install --save cruddl

Install ArangoDB and create a new database.

import { ArangoDBAdapter } from 'cruddl';
const db = new ArangoDBAdapter({
    databaseName: 'databaseName',
    url: 'http://root:@localhost:8529',
    user: 'root',
    password: '',
});

If you just want to explore the features, you can also use an in-memory database implementation - but don't use this for anything else.

import { InMemoryAdapter } from 'cruddl';
const db = new InMemoryAdapter();

Define your data model and create a project:

import { Project } from 'cruddl';
const project = new Project({
    sources: [
        {
            name: 'schema.graphqls',
            body: `
            type Movie @rootEntity {
              title: String
              actors: Actor @relation
            }
            
            type Actor @rootEntity {
              name: String
              movies: Movie @relation(inverseOf: "actors")
            }`,
        },
        {
            name: 'permission-profiles.json',
            body: JSON.stringify({
                permissionProfiles: {
                    default: {
                        permissions: [
                            {
                                roles: ['users'],
                                access: 'readWrite',
                            },
                        ],
                    },
                },
            }),
        },
    ],
    getExecutionOptions: ({ context }) => ({ authContext: { authRoles: ['users'] } }),
    getOperationIdentifier: ({ context }) => context as object, // each operation is executed with an unique context object
});

Then, create the GraphQL schema and serve it:

import { ApolloServer } from 'apollo-server';
const schema = project.createSchema(db);
db.updateSchema(project.getModel()); // create missing collections
const server = new ApolloServer({
    schema,
    context: ({ req }) => req, // pass request as context so we have a unique context object for each operation
});
server.listen(4000, () => console.log('Server is running on http://localhost:4000/'));

See the modelling guide and the api documentation for details.

Usage in a browser environment

The core of cruddl perfectly works in a browser (e.g., using webpack), and this can be useful to generate a mock GraphQL schema on the fly or to validate a cruddl project. However, the ArangoDB adapter only works with node imports like path. Unless you configure webpack to provide mock modules for them, you will get an error when you import cruddl in a webpack environment. To solve this, you can import the core symbols from cruddl/core and the InMemoryAdapter from cruddl/inmemory.

Running Tests

For consistency, tests shall be run against a single arangodb node:

  1. npm i
  2. npm run start_arangodb
  3. ensure you have access to console at http://localhost:8529
  4. npm run test

When done, stop the instance with npm run stop_arangodb

Documentation

Supported versions

cruddl currently supports the following versions of ArangoDB:

  • 3.8
  • 3.9
  • 3.10
  • 3.11

cruddl's People

Contributors

crowgames avatar dependabot[bot] avatar etienne-buschong avatar henkesn avatar joerg84 avatar kroeder avatar mfusser avatar niehno avatar rmortes avatar simhnna avatar yogu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cruddl's Issues

Apollo Federation and ArangoSearch

Is Cruddl compatible with Apollo Federation? if so are there any working examples of it?

And also, is there any plans to support ArangoSearch in the future?

How to handle createdAt and updatedAt for pre-existing documents

If the collection contains pre-existing records, and those records do not contain createdAt or updatedAt and I try to return those fields in a query, I get an error Cannot return null for non-nullable field Order.createdAt.

What is the expected way to deal with this?

Unsupported filter pattern for graph traversal error thrown when using permissions profile.

Hello,

I'm getting Unsupported filter pattern for graph traversal ($Product_66.ctx.businessUnit IN literal [ "320859222"]) when I use the following permissions profile.

entityName:
  permissions:
      - access:
          - create
          - read
          - update
        roles:
          - /^editor-bu-(.*)$/
        restrictions:
          - field: ctx.businessUnit
            valueTemplate: $1

The entities have a ctx entity extension with businessUnit field. I'm hoping you can give me some information on why this might be occurring and how I might be able to resolve.

Look forward to hearing back.

Project status / roadmap

I have actively been following the development of Cruddl for a while now, I am eager to get involved in the development process, but there is a lot of missing information such as the project objectives, roadmap, indepth documentation (w/ Examples) and more.

Key features such as:

  • Subscriptions (#128)
  • Live Queries
  • Seeding (#89)
  • Custom resolver logic (like with Prisma)
  • Apollo Federation support (#101)
  • ✅ArangoSearch Integration
  • Foxx Service

Other factors to consider are:

  • Documentation
  • Examples
  • Boilerplates
  • Blog (with Updates / Progress?)

All these would not only boost the project's awareness but also use cases and contributions.

Dynamic arangodb database change

Hello.
It's possible to change the database name based on context?
I want to change the database name based in the request headers, it will be nice to do something like this:

import { ArangoDBAdapter } from "cruddl";
import { ApolloServer } from "apollo-server";
const schema = project.createSchema(db);
db.updateSchema(project.getModel()); // create missing collections
const db = new ArangoDBAdapter({
  databaseName: "databaseName",
  url: "http://root:@localhost:8529",
  user: "root",
  password: ""
});
const server = new ApolloServer({
  schema,
  context: ({ req }) => { 
    const {tenantId} = jwt.verify(req.headers.authorization);
    db.setDatabseName(tenantId);
    return req;
  } 
});
server.listen(4000, () =>
  console.log("Server is running on http://localhost:4000/")
);

It very useful for build multi tenant system with database isolation.

A few questions

First of all I would like to thank you for the work you have done on this lib. I am quite excited that I found it, I was looking for ArangoDB support in prisma but it's not available.

Questions:

  1. Is there any roadmap for the v1 release? - maybe I could help with something
  2. Is there an ETA for #74? - ArangoSearch is a must have in my case
  3. Is it possible to create the schema with TypeGraphQL? - I think it should be but not sure
  4. What is the best way to use this lib? - Embedding it in a foxx service or just adding it on the server?

Puzzled - relations

Hi,

First and foremost, thank you for this great piece of software. Great to have arangodb schema generation from a graphql IDL.

Please consider the following simplified schema:

type Identification @rootEntity {
  number: String!
  status: String!
  party: Party! @relation
}

type Party @rootEntity {
  formattedName: String
  firstName: String
  lastName: String
  identifications: [Identification] @relation(inverseOf: "party")
}

Note the relationships between Party & Identifications. Which can be summarized as follows:

  • A Party can have 0..* Identifications
  • Identifications always related to one Party

Now, when Cruddl processes the schema, it creates this structure

type CreatePartyInput {
formattedName: String
firstName: String
lastName: String
identifications: [ID!]
createIdentifications: [CreateIdentificationInput!]
}

It looks like identifications is now a required field...
More generally... how would you use the mutations to create parties & Identifications?

?! Am I missing something...

Thank you

Seeding (and CLI)

Are there any plans to support seeding for Cruddl? as projects like Prisma currently do

If seeding was to be supported would Cruddl have a CLI? as that would be amazing to see

Apollo Integration

Hello,

I used to use Prisma alot with Apollo, and I'd like to completely replace Prisma with Cruddl, but I have a few questions:

The reason for asking this is because currently (as far as the docs go), I'd have to setup a GraphQL server (Yoga or Apollo) with Cruddl from a GraphQL schema thats written by the developer, then another graphql server has to be made with custom resolvers, and rewrite the typeDefs (too much repetition) to get it all working the way prisma operates now.

Using the schema file as the typeDefs for Apollo server results in missing directives errors

Is there any way to shorten this process? i.e. Ability to overwrite resolvers in the schema generated by Cruddl as I'm aware that Apollo has addResolveFunctionsToSchema({ schema, resolvers }) however there is no API to access the data directly i.e. with Prisma you could do something like:
users: (parent, args, ctx, info) => ctx.db.users(args, info)

  • Seeding (already answered on: #89)

Forgive me if the issue is straight forward, I've spent a few days playing around, researching, reading through the docs but this issue isn't referenced anywhere

Sparse indexing

I'm trying to mark multiple fields as @key, but I'm running into a problem with the unique indexing happening on default for fields used as @key. How might one go about adding/enabling Arango's sparse indexing feature for unique indices? I'm a little lost looking at the move-up-field-indices-transformer but I think I'm in the right place...?

Cruddl with Typescript project

Having a heckuva time even following your demo/setup code in the Readme due to Typescript errors on cruddl dependencies. Getting errors such as:

node_modules/graphql-yoga/dist/types.d.ts:8:29 - error TS2307: Cannot find module 'apollo-server-core'.
8 import { LogFunction } from 'apollo-server-core';

I've tried mucking with my tsconfig.json and importing several different @types/ to my package.json to no avail. What am I missing?

Can we use loadProjectFromDir()?

I found that function, and tracked down that it is used in tests, but I think it would be useful to have available in our actual project.

That way, we could have one or more directories with .graphqls and permission.json files in it instead of either defining them statically in the source, or writing the same file handling code you already have.

Thoughts?

Collect last entity in chain

Hi! I'm fairly new to graph databases, so I may not know the correct terminology.

If I have a schema like this one:

Schema
graph TD
    A[Employer] -->|Worked with| B(Employee 1)
    A[Employer] -->|Worked with| C(Employee 2)
    A[Employer] -->|Worked with| D(Employee 3)
    A[Employer] -->|Worked with| E(Employee 4)
    A[Employer] -->|Worked with| F(Employee 5)
    B -->|Taught| C
    C -->|Taught| D
    D -->|Taught| E
    E -->|Taught| F

Is there a way to retrieve the last entity, Employee 5, using this library?

Also, my schema may not be as "pure" as that one, looking more like:

Schema
graph TD
    A[Employer] -->|Worked with| B(Employee 1)
    A[Employer] -->|Worked with| C(Employee 2)
    A[Employer] -->|Worked with| D(Employee 3)
    A[Employer] -->|Worked with| E(Employee 4)
    A[Employer] -->|Worked with| F(Employee 5)
    A[Employer] -->|Worked with| G(Employee 6)
    B -->|Taught| C
    C -->|Taught| D
    D -->|Taught| E
    E -->|Taught| F
    B -->|Taught| G
    C -->|Taught| H(Employee 7)

But I'd still want to retrieve the lastof the "teaching" chain for a specific employer

Thanks!

GraphQL scalar definition not supported

cruddl appears to be throwing errors on Graphql documentation syntax. If my schema has something like this:


"""
IS08601 time duration
"""
scalar Duration

Processing through cruddl reports errors like this:

[error]: uncaughtException: Project has errors:
Error: This kind of definition is not allowed. Only object and enum type definitions are allowed. at schema.graphqls:2:1

Input validation

Does cruddl support any type of input validation and/or munging hooks or other mechanisms to ensure that mutation input is valid prior to operating against the back end database?

Imperformant queries on flex search with cursor

Using cursor based navigation produces queries which require to load too much data. Having the _cursor in the result set is sufficient to have the imperformant query. Simplest query to reproduce

query {
    flexSearchShipment(first: 1) {
      _cursor
    }
}

Sorting nested fields

I would like to be able to sort nested fields.

Here is an example of our schema:

type Product @childEntity {
  name: String
}

type Component @rootEntity {
  products: [Product]
}

I have a ui that list the components and would like to be able to sort the Component by product name. Is it possible to have an orderBy field like products_product_name_asc?

Thanks!

How to compile?

Hi, trying to compile the library with npm run build but getting all kinds of errors:

Getting:

spec/model/implementation/root-entity-type.spec.ts:410:45 - error TS2345: Argument of type '{ kind: TypeKind.ROOT_ENTITY; name: string; fields: { name: string; typeName: string; }[]; }' is not assignable to parameter of type 'RootEntityTypeConfig'.
  Types of property 'fields' are incompatible.
    Type '{ name: string; typeName: string; }[]' is not assignable to type 'ReadonlyArray<FieldConfig>'.
      Types of property 'concat' are incompatible.
        Type '{ (...items: ConcatArray<{ name: string; typeName: string; }>[]): { name: string; typeName: string; }[]; (...items: ({ name: string; typeName: string; } | ConcatArray<{ name: string; typeName: string; }>)[]): { ...; }[]; }' is not assignable to type '{ (...items: ConcatArray<FieldConfig>[]): FieldConfig[]; (...items: (FieldConfig | ConcatArray<FieldConfig>)[]): FieldConfig[]; }'.
          Types of parameters 'items' and 'items' are incompatible.
            Type 'ConcatArray<FieldConfig>' is not assignable to type 'ConcatArray<{ name: string; typeName: string; }>'.
              Type 'FieldConfig' is not assignable to type '{ name: string; typeName: string; }'.
                Property 'description' does not exist on type '{ name: string; typeName: string; }'.

410             const type = new RootEntityType({
                                                ~
411                 kind: TypeKind.ROOT_ENTITY,
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
418                 ]
    ~~~~~~~~~~~~~~~~~
419             }, model);
    ~~~~~~~~~~~~~

Am I doing anything wrong? I do run TS 3.0.3

Tks

Authorization Issues

Hi,

I can stand up the example schema and server in README, but cannot get queries to resolve due to an authorization error. Can someone advise?

Script

const { ArangoDBAdapter, Project } = require("cruddl");
const { GraphQLServer } = require("graphql-yoga");

const project = new Project([
  {
    name: "schema.graphqls",
    body: `
    type Movie @rootEntity {
      title: String
      actors: Actor @relation
    }
    
    type Actor @rootEntity {
      name: String
      movies: Movie @relation(inverseOf: "actors")
    }`
  },
  {
    name: "permission-profiles.json",
    body: JSON.stringify({
      permissionProfiles: {
        default: {
          permissions: [
            {
              roles: ["users"],
              access: "readWrite"
            }
          ]
        }
      }
    })
  }
]);

const db = new ArangoDBAdapter({
  databaseName: "aqqaint",
  url: "http+tcp://root:@0.0.0.0:8529",
  user: "root",
  password: ""
});

const schema = project.createSchema(db);
db.updateSchema(project.getModel()); // create missing collections
const server = new GraphQLServer({ schema, context: () => ({ authRoles: ['users'] }) });
server.start(() => console.log('Server is running on http://localhost:4000/'));

Query

{
  allMovies{
    id
  }
}

Console Output:

schema-builder: Schema created successfully. []
Server is running on http://localhost:4000/
ArangoDBAdapter: ArangoDB version: 3.4.1. Workaround for sparse indices will not be enabled. []
query-resolvers: Executing query  []
query-resolvers: Operation: {
  allMovies {
    id
  }
} []
query-resolvers: DistilledOperation: query with selections {
  "allMovies": field allMovies with selections {
    "id": field id
  }
} []
query-resolvers: Request roles:  []
query-resolvers: Before authorization: {
  "allMovies": entities of type Movie as list with $movie_2 => 
    as {
      "id": id($movie_2)
    }
} []
query-resolvers: After authorization: {
  "allMovies": error "AuthorizationError: Not authorized to read Movie objects"
} []
ArangoDBAdapter: Executing AQL: @v_result1 = execute(
  RETURN {
    "allMovies": {__cruddl_runtime_error: "AuthorizationError: Not authorized to read Movie objects"}
  }
); []
query-resolvers: Execution successful []
query-resolvers: Result: {
  "allMovies": {
    "__cruddl_runtime_error": "AuthorizationError: Not authorized to read Movie objects"
  }
} []
Error: AuthorizationError: Not authorized to read Movie objects
    at Object.extractRuntimeError (/Users/michael/code/src/0001-Aqqaint/db/node_modules/cruddl/dist/src/query-tree/errors.js:40:12)
    at result.then.res (/Users/michael/code/src/0001-Aqqaint/db/node_modules/cruddl/dist/src/schema/preparation/schema-transformation-modules/add-runtime-error-resolvers.js:16:56)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Interface in Array

First of all thank you for that project,

I want to store a general class type which is called Children in an array and it can includes Screen or Test inside Module Type. Normally, graphql and some popular clients for db integrations supports those interface type but looks like not in cruddl. How can I achieve that purpose? Based on that feature, we will use that project in a large-scale project.

 interface Children{
      name: String
 }

 type Module @rootEntity {
      childrens : [Children] @relation(inverseOf: "parent")
 }
      
 type Screen implements Children @rootEntity{
      name : String
      //it will include some different variables
      parent: Module @relation
 }

 type Test implements Children @rootEntity{
      name : String
      //it will include some different variables
      parent: Module @relation
 }

Does not work in tandem with other directives

Cruddl throws up if you also reference other non-cruddl directives in your schema. By graphql standard, @Directives are supposed to be chainable from left-to-right. Instead, cruddl throws errors if it sees a directive it doesn't understand. Is there a way to pass other directive handlers into cruddl so it knows about the use of other non-cruddl directives such as graphql-constraint-directive?

Using @reference for 1:n relationships

Hi cruddl team! Thank you for all of your great work on this project. I apologize in advance for the length of this issue, but want to make sure I'm providing as much context as possible.

We currently have a scenario where we're leveraging the @relation directive for a number of 1:n relationships. This has given us a lot of flexibility in our API in terms of being able to fetch for entities in both directions of that relationship. What we're seeing as our collections have grown in size, though, is slow performance when those edges are leveraged, particularly when used in filtering.

Example of current schema:

type House @rootEntity {
  description: String
  people: [Person] @relation(inverseOf: "house")
}

type Person @rootEntity {
  name: String
  house: House @relation
}

We really like that this allows us to do both of the following queries:

query {
  allHouses {
    id
    description
    people {
      id
      name
    }
  }
  allPeople {
    id
    house {
      id
      description
    }
  }
}

Where this had led to slow performance for larger collections, is that querying for allPeople that are associated with a particular House.id generates the following AQL:

WITH houses
RETURN {
  "allPeople": (
    FOR v_person1
    IN people
    FILTER (FIRST((
      FOR v_node1 // this nested traversal is costly 
      IN OUTBOUND v_person1 people_house
      FILTER v_node1 != null
      RETURN v_node1
    ))._key IN ["some_id"])
    RETURN {
      "id": v_person1._key,
      "name": v_person1.`name`
    }
  )
}

The filtering in that query has been inefficient on large collections because of the graph traversal, regardless of how optimized the indices are. We know that we can solve this by leveraging @reference and bypassing the edge traversal when filtering in those scenarios. Updating the schema to:

type HouseWithRef @rootEntity {
  description: String
  uuid: String @key
}

type PersonWithRef @rootEntity {
  name: String
  houseUuid: String
  house: HouseWithRef @reference(keyField: "houseUuid")
}

and running a query like:

query AllPeople($filter: PersonWithRefFilter) {
  allPersonWithRefs(filter: $filter) {
    id
    name
    house {
      id
      description
  }
}

{
  "filter": {
    "houseUuid_in": ["some_id", "another_id"]
  }
}

generates the following AQL, which is much more efficient:

RETURN {
  "allPersonWithRefs": (
    FOR v_personWithRef1
    IN personWithRefs
    FILTER (v_personWithRef1.`houseUuid` IN ["some_id","another_id"])
    LET v_houseWithRef1 = (IS_NULL(v_personWithRef1.`houseUuid`) ? null : FIRST((
      FOR v_house1
      IN houseWithRefs
      FILTER ((v_house1.`uuid` > NULL) && (v_house1.`uuid` == v_personWithRef1.`houseUuid`))
      LIMIT 1
      RETURN v_house1
    )))
    RETURN {
      "id": v_personWithRef1._key,
      "name": v_personWithRef1.`name`,
      "house": (IS_NULL(v_houseWithRef1) ? null : {
        "id": v_houseWithRef1._key,
        "description": v_houseWithRef1.`description`
      })
    }
  )
}

The downside to doing this is that you lose the queryability in both directions that leveraging @relation provides (e.g. querying for people in a house). So finally my question, is there a way in cruddl to leverage @reference and generate the resolvers for resolving entities in the opposite direction of the reference - essentially a @reference-based version of @relation(inverseOf: "")? I know the modeling docs mention "In contrast to relations, it is however not possible to navigate from the referenced object to the referencing object", but I wanted to check with you all.

If there's not a way to do this in cruddl, have there been any discussions on adding support for it? This can be done in pure AQL and would really help improve the performance when using cruddl for things that are a 1:n relationship. From the Arango docs:

ArangoDB does not require you to store your data in graph structures with edges and vertices, you can also decide to embed attributes such as which groups a user is part of, or _ids of documents in another document instead of connecting the documents with edges. It can be a meaningful performance optimization for 1:n relationships, if your data is not focused on relations and you don’t need graph traversal with varying depth.

Finally, is there anything else that's lost when switching a relationship from @relation to @reference aside from being able to query in both directions?

Please let me know if there's any other context I can provide, and thank you again for all of your great work on this project!

support arbitrary paging

atm cursor-based paging is supported (first and from).

Would be great to also query an arbitrary page; something like:

query {
  allOrders(filter: ..., orderBy: ..., start: 20, first: 10) {
    orderNumber
    _cursor
  }
}

to select 10 items, starting from the 20th item.

[BUG] Named query cache issue

The result of a named query gets cached the first time it runs and the cache seems to ignore the variables being passed along with the query.

Subsequent runs of the same named query with different variables seem to return the cached result of the first query run.

I have tried to reproduce this issue here but I can't so this might not even be an issue with cruddl.

This is my current setup:

import { Project, ArangoDBAdapter } from 'cruddl'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { SchemaLink } from 'apollo-link-schema'
import env from '../utils/env'
import dbSchema from './schema.graphqls'

let dbClient: ApolloClient<unknown>

const initDBGQLClient = () => {
    const db = new ArangoDBAdapter({
        databaseName: env.DB_DATABASE,
        url: env.DB_URL,
        user: env.DB_USERNAME,
        password: env.DB_PASSWORD
    })

    const project = new Project({
        sources: [
            {
                name: 'schema.graphqls',
                body: dbSchema
            },
            {
                name: 'permission-profiles.json',
                body: JSON.stringify({
                    permissionProfiles: {
                        default: {
                            permissions: [
                                {
                                    roles: ['any'],
                                    access: 'readWrite'
                                }
                            ]
                        }
                    }
                })
            }
        ],
        getExecutionOptions: () => ({ authRoles: ['any'] })
    })

    // create missing collections then return client
    return db.updateSchema(project.getModel()).then(() => {
        dbClient = new ApolloClient({
            cache: new InMemoryCache(),
            link: new SchemaLink({ schema: project.createSchema(db) })
        })

        return dbClient
    })
}

export { initDBGQLClient, dbClient }

Sample query:

dbClient.query({
    query: gql`
        query GET_USER_WITH_USERNAME($username: String!) {
            allUsers(filter: { username: $username }, first: 1) {
                id
                username
            }
        }
    `,
    variables: {
        username
    }
})

Sample query that works:

dbClient.query({
    query: gql`
        query {
            allUsers(filter: { username: "${username}" }, first: 1) {
                id
                username
            }
        }
    `
})

As you can see, I am not using cruddl with a graphql server, instead I'm passing the schema directly to the apollo client using apollo-link-schema. Does cruddl only work with a graphql server? I don't see why this setup would cache a named query on the first run ignoring the variables.

I have asked about GraphQL to AQL query memoization and the answer (#80 (comment)) was that it's not implemented but I'm not sure if cruddl performs any result caching.

Why not focus on graph databases?

I've read previously that you trying to remain db agnostic, therefore you can't entirely support graph features. Why bother competing with the 3 other solutions for sql databases (hasura)?

Permisions claim check field with multiple values?

Hello,

I am currently trying to setup a permissions-profile and was hoping to get some feedback on how to handle permissions for referenced entities and/or suggestions on model design.

For example I have something similar to the following:

type Gallery @rootEntity(permissionProfile: “gallery”) {
	name: String!
	portfolios: [Portfolio!] @relation
	gallery: !ID @accessField
}
type Portfolio @rootEntity {
	name: String!
	images: [Image!] @relation(inverseOf: “portfolio”)
}
type Image @rootEntity {
	name: String!
	url: String!
	portfolio: Portfolio! @relation
}
permissionProfile:
  gallery:
    - access: read
      roles:
        - viewer
      restrictions:
        - field: gallery
          claims: galleries

A user with read permissions would be able to access the name field but would get “Not authorized to read Portfolio objects (in Gallery.portfolios)“ when trying to access porfolios of the gallery . The only way I can think to get around this is to add a Ctx extension to each entity and update the permissions-profile to reference the new context field / or create seperate permission for each entity with the correct feld, claim combination.

type Ctx @entityExtension {
	galleryId: ID @accessField
	portfolioId: ID @accessField
	ownerId: ID @accessField
}
type Gallery @rootEntity(permissionProfile: “gallery”) {
	name: String!
	portfolios: [Portfolio!] @relation
	ctx: @accessField
}
type Portfolio @rootEntity(permissionProfile: “portfolio”) {
	...
	ctx: Ctx @accessField
}
type Image @rootEntity {
	...
	ctx: Ctx @accessField
}
permissionProfile:
  gallery:
    - access: read
      roles:
        - viewer
      restrictions:
        - field: ctx.gallery
          claims: galleries
  portfolio:	 
     - access: read
        roles:
          - viewer
       restrictions:
         - field: ctx.gallery
           claim: galleries
         - field: ctx.portfolio
           claim: portfolios

Adding Ctx entity solved the authentication issue but a Portfolio in the example can be referenced by many Gallery entities. A a user with access to one gallery and not the another would get access denied when trying to access portfolio in current setup unless ctx.gallery was an array.

I know it is not possible to to have an array as a field value. Is there something I can do that would resolve the scenario mentioned above. Something like any([claim in field for claim in claims]) if isinstance(field, list) else field in claims

Do I need Ctx or is there a better way to handle this use case ?

I am still learning my way around so any help would be appreciated.

Regards,
John

Support graphql 0.12 / 0.13

Version 0.12 introduced the ExecutableDefinitions which only accepts operation and fragment definitions. We should opt of this out when running the standard GraphQL validation rules.

Is there a solution to pass the (permissions related) "userId" to mutated objects?

Scenario: A user creates an object in a collection. The resolvers shoudl inject the "user-id" into the field that is also used for the permissions to display only that user's related records. This is needed in order to prevent the ability of database tampering by clients, who could inject other ids if the clients are to provide said ids.
This is also needed for related collections, which when a mutation is creating or updating or removing objects, should pass said ids to all collections so that only user-related objects are mutated or returned in queries.

I have not found this kind of functionality. Is there something that I could use to achieve this or if not, is there anything planned?
Thank you very much

Unable To Complie With Typescript

Trying to build my Typescript project that uses this package throws an error.

node_modules/cruddl/dist/src/execution/schema-executor.d.ts:7:10 - error TS2440: Import declaration conflicts with local declaration of 'SchemaExecutionArgs'.

import { SchemaExecutionArgs } from './schema-executor';
           ~~~~~~~~~~~~~~~~~~~

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

Schema generation removing non-null rules

We've noticed that the final GQL schema generated by Cruddl seems to be lacking non-null (!) rules that are present in the original model.

For example:

type Group @rootEntity(flexSearch: true, flexSearchLanguage: EN) {
  name: String!
  type: String
}

generates:

type Group {
  """
  An auto-generated string that identifies this root entity uniquely among others of the same type
  """
  id: ID!

  """The instant this object has been created"""
  createdAt: DateTime!

  """
  The instant this object has been updated the last time (not including relation updates)
  """
  updatedAt: DateTime!
  name: String
  type: String

  """
  Provides a value that can be supplied to the `after` argument for pagination. Depends on the value of the `orderBy` argument.
  """
  _cursor: String

  """
  An identifier that is updated automatically on each update of this root entity (but not on relation changes)
  """
  _revision: ID!
}

The same issue applies to the mutation input types, e.g. CreateGroupInput as well.

Logging configuration

What is the right way to handle configuration of the logger?
Based on notes in the source code, I tried bringing in log4js as a dependency and then changing the log level like this:

const log4js = require('log4js');
log4js.getlogger().level = 'warn';
log4js.getLogger('ArangoDBAdapter').level = 'warn';

const project = new Project({
  loggerProvider: log4js,
        sources: [
...

But even with that, I still get a log message from the ArangoDBAdapter:
ArangoDBAdapter: Executing AQL: @v_result1 = execute(

Is it right for me to be bringing in log4js as a dependency? it doesn't look like the built-in ConsoleLoggerProvider is exported, nor is it actually configurable, so I'm thinking that part is right.

Cruddl compatibility with multi-node ArangoDB clusters

Hi, is Cruddl expected to generate AQL that is compatible with anonymous graph traversals on multi-node clusters? It seems as if the generated AQL is missing the necessary WITH <collections> stanza, resulting in error messages like the following:

Query: AQL: Error message received from cluster node 'PRMR-tn50ynem': collection not known to traversal: 'organizations'. please add 'WITH organizations' as the first line in your AQL (while executing)

Is it possible to ignore permissions check on a @relation and have id accessible to the permissions-profile?

I would like to be able to assign a permission profile to a single entity without having to define seperate profile to check entity relation.

For example I have something similar to following:

type Ctx @valueObject {
  id: ID
  pfl: ID
  gal: ID
}

type Portfolio @rootEntity(permissionProfile: "portfolio") {
  gallery: Gallery! @relation
  tags: [Tag] @relation
  ctx: Ctx @accessField
}

type Tag @rootEntity {
  name: String
  ctx: Ctx @accessField
}

type Gallery @rootEntity(permissionsProfile: "gallery") {
   potfolios: [Portfolio]
   ctx: Ctx @accessField
}

permissions-profile.yaml

permissionProfile:
  portfolio: 
    - access: read
      roles:
        - /^viewer-gal-(.*)$/
      restrictions:
        - field: ctx.gal
          valueTemplate: $1
   - access: read
     roles: 
       - /^viewer-pfl-(.*)$/
     restrictions:
       - field: ctx.pfl
         valueTemplate: $1

In this example if I where to try and access tags I would get message Not authorized to read Tag objects (in Portfolio.tags)", To allow user access to the referenced entity I would need to add another policy for tag.

Is it possible to avoid having to add additional policy for each reference. If a permission profile is not defined for the referenced entity can the check be skipped?

I was also curious about the id field. Should it be accessible to permissions-profile. At the moment I am unable to reference id even when I define it in the schema with id: ID @key @accessField. I have getting around many of these things by adding a context field and giving it a uuid field.

Any help would be appreciated. Look forward to hearing back.

Thank you,
John

Decorators for code first implementations.

Nestjs has a really good graphql resolver system and I think this would be a great extension of that with the ability to map the relationships of Arango graphs.

What i'm asking for:
Creation of decorators for @unique, @RootEntity, @references, etc...

Extended ask:
Can a plugin for the @nestjs/graphql package be made? This would make an amazing pairing.

Possible to alias a collection name?

I just discovered that someone created the collection I was attempting to run cruddl over with an initial capital instead of all lowercase. i.e. Orders instead of orders.
I've re-read the modelling doc and I don't see any way to point a root type at a particular collection. Is this possible?

Custom Edge Names

I already have a project that uses ArangoDB, I wanted to use Cruddl for a new feature. The Data Modeling doc states that:

In ArangoDB, relations are stored in an edge collection with the name orders_customer (the plural of the source object combined with the field name).

However I have a different approach designing my db regarding edge names and would be really nice to be able to give custom names to relationships. ie:

type Customer @rootEntity {
  name: String
  orders: [Order] @relation(edge: "has_ordered", inverse_edge: "was_ordered_by")
}

Also it was unclear whether it is possible to give custom attributes to edges besides _from and _to.

Cascading deletes

Does cruddl have any existing capability or roadmap plans for supporting "cascading deletes" between root entities? What happens currently with edge collections references documents when a vertex is deleted?

Responses being aggressively cached, preventing mutation operations

I'm seeing unexpected mutation behavior when attempting to use the simple example schema from the README. I have this in GraphQL playground:

mutation {
  createMovie(input: {
    title: "Test"
  }) {
    id
    title
  }
}

First execution worked as expected. Arango shows a new document and the GraphQL response shows the newly generated document ID.

However, attempts to run the same query a second time do NOTHING. I've refreshed the GraphQL playground and run again. The server console shows nothing at all.

Even worse, I removed the original document from the Arango admin and tried again. This time the Playground responded as if it had created the document, returning the same ID as before. I then went into Arango and checked. The document does NOT exist.

Is cruddl attempting to keep some type of in-memory cache that is out of sync with Arango?

Lack of support for GraphQL Unions and Interface

We have need to support Interfaces so a collection may reference a root entity in more than one other collection. I know you don't support this right now. Any suggested workaround at the moment?

Establish entity relationships using natural keys

First of all, thank you so much for creating and maintaining this excellent library!

I'm working in a domain where we have to populate a significant amount of reference data that has "natural" keys, and then relate other entities to that reference data in an ETL-type data ingest tool.

Unless there is a better approach that I'm not understanding, it seems like we have to do a two-step process to build the necessary relationships to entities that have a natural key:

  • look up their GQL/Cruddl/ArangoDB id (surrogate key), by using the natural key
  • use the id (surrogate key) when creating the mutation to insert/update the related entity.

Is there any way that we can create the relationship to the reference data entity directly by its natural key instead of the surrogate key?

Example model:

type ReferenceEntity @rootEntity(flexSearch: true, flexSearchLanguage: EN) {
  """
  The natural key of the entity
  """
  entityId: String! @key
}

type IngestedEntity @rootEntity {
  referenceEntity: ReferenceEntity @relation
  someOtherField: String!
}

The ingest client has to do something like...

Fetch the ID by the natural key

query ($filter: ReferenceEntityFilter) {
  allReferenceEntities(filter:$filter) {
    id
  }
}
{
  "filter": {
    "entityId": "MyEntityId"
  }
}

And then use that id in any subsequent mutation.

mutation ($input:CreateIngestedEntityInput!) {
  createIngestedEntity(input:$input) {
    id
    referenceEntity {
      id
      entityId
    }
  }
}
{
  "input": {
    "someOtherField": "abc",
    "referenceEntity": 1234
  }
}

Where instead, it would be fantastic if there was some way that we could express this mutation with the natural key, e.g.

{
  "input": {
    "someOtherField": "abc",
    "referenceEntity": "MyEntityId"
  }
}

Thanks in advance for your help!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.