<!--- <p align="center">
  <img width="300" src="https://github.com/remorses/mongoke/blob/master/.github/logo.jpg?raw=true">
</p> --->
<h1 align="center">skema</h1>
<h3 align="center">Single source of truth for all your types</h3>

## Why

Today multi-service architectures requires developers to keep in sync a lot of different services built in different languages by different teams.
To do this manually requires a lot of work always changing the shared object types between different projects and a lot of integration tests to make sure that all the services can communicate.
With skema you can have one single source of truth for your most important shared types and can generate the validation (jsonschema) and the code to serialize them and be sure the different services can communicate.

## Supported languages
### built-in
- **jsonschema**
- **python**
- **graphql**
### using [quicktype](https://github.com/quicktype/quicktype)
- **python**
- **typescript**
- **go**
- **rust**
- **c++**
...

## Installation
Requires python 3.6+ and npm
```
pip install skema
npm i -g quicktype # for more languages
```

## Usage
```
skema generate ./schema.skema --jsonschema ./your_path.json
skema generate ./schema.skema --graphql ./your_path.graphql
skema generate ./schema.skema --typescript ./your_path.graphql
# using an hosted skema
skema generate "https://gist.github.com/your_gist" --typescript ./your_path.graphql
```


## Examples

## Nested objects
```yml
User:
    id: Int
    name: Str
    address:
        street: Str
        number: Int
        state: Str
    credit: Float
    email: /.*@.*\.com/
```

## Lists
```yml
Pet:
    name: Str
    friends: [Pet] # you can reference other types

Owner:
    full_name: Str
    hobbies: [
        name: Str
        when: CronString
    ]

CronString: Str
```

## Unions
```yml
Animal: Tiger | Bear | Panthera

Panthera:
    _id: ObjectId # a type alias
    black_nuance: "super_dark" | "dark" | "light" # enumeration

Tiger:
    _id: ObjectId
    humans_killed: Int

Bear:
    _id: ObjectId
    likes_honey: Bool

ObjectId: Any
```

## And types
```yml
Centaur: Horse & Human

Horse:
    name: Str
    eats: ["carrots" | "weeds"]

Human:
    name: Str
    surname: Str
    eats: ["meat" | "vegetables"]
```





## Built in types
- Int
- Float
- Str
- Bool
- null
- "literal string"
- /regex/
- 0..69
- Any



## What you can do with a skema file:
- Validate json input
- Generare code types for every language in your architecture (graphql, py, ts, cpp, ...)
- Generate fake data based on your schema
- Infer schema from raw json (perfect for reverse engineering)
- convert jsonschema to be easier to read
- use it for API types documentation
- Generate react forms, via [`react-skema-forms`]
- use it to plan your domain model!


## spec

- all root properties are references and can be used as types
- types can be object whose properties are expressed as key: value or other primitive types like 
    - Str,
    - Int,
    - Bool,
    - Float
    - /regex/
    - 0..100 (int range)
    - .0..1 (float range)
- type inside [ ] is an array type
- types can be mixed together: 
    - `Str | Int` means one of string and int
    - `Object1 & Object2` means "all the properties of object 1 and 2"
    - `Object1 | Object2` means "properties of object 1 and 2"
- types can be annotated writing annotations """ quotes above the definition
- additional propertiescan be specified adding ... at the end of an object and can be better shaped treating it like a normal key, ...: Str means additional properties must be Str


## todo:
- optional types are union of null ans the type in jsonschema
- scalar name can be overridden by other splitted types in py and gql
- when generating python i have to order dependencies over the types
- ~~scalar unions with null should be removed from graphql~~
- --hide [...] should support other types than scalars and opther lans other than gql
- ~~specify if a type gets translated as input, interface in graphql, [graphql input]
- spaces between properties cause properties spaced to be taken at an outer level
- graphql doesn't support union inside unions, so i must unnest them
- in graphql enums with options > 2 are taken as strings
- if i use Root as a name, this is removed from graphql
- when using from_jsonschema the i have to dereference anyOf, oneOf, allOf and enums. These can share same name (parent.value + property) and can conflict in final skema
- when dereferencing skema to produce graphql i am adding parent  names to differentiate the final type names, i am not sure this can really work in long term
- when creating python code, special keywords (from) are padded with parent names, this means i can't use `ObjectName(**data)`
- python code translates const enums as an enum object of a single value, it should search for other equal common enum ref (can be solved using common interface)
- | and & don't work with [], because VAl is split with ARRAY and smaller VAL during tokenization, is hould add a rule during tokenization to exclude | and & in array tokens and add array logic inside VAL handling (can be solved putting [] only to a reference)
- the same for regex
- better handling of comments and white space
- dynamic indentation (now is set as 4 spaces)
- ~~in gql remove types used in & operations, given that thay are merged~~





bugs:
- if key name is equal to another type name then fuck it up
- can't write `key: [Str] | Int`
- objects get additionalProps: true by default even if they haven't ... at the end like:
```
Object:
    a: Str
    b: Int
    ...
```
this is because i always forget to put them when doing `Object1 & Object2` so i just removed it



graphql todos:
- remove useless parent nesting in names if possible
- assert no other types exist before generating one
- customization for Interface_end_name, 
