Skip to content

Commit bd60786

Browse files
Merge branch 'data-model-separation' into brapi-V2.1
2 parents 7a32fe9 + 75291cb commit bd60786

File tree

136 files changed

+40549
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+40549
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Java CI with Gradle
2+
3+
on:
4+
push:
5+
workflow_dispatch:
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
- name: Set up JDK 21
12+
uses: actions/setup-java@v4
13+
with:
14+
java-version: '21'
15+
distribution: 'temurin'
16+
- name: Change wrapper permissions
17+
working-directory: ./generator/
18+
run: chmod +x ./gradlew
19+
- name: Validate
20+
working-directory: ./generator/
21+
run: ./gradlew validate
22+
- name: Generate OpenAPI, GraphQL and OWL
23+
working-directory: ./generator/
24+
run: ./gradlew generateAll
25+
- uses: stefanzweifel/git-auto-commit-action@v5

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
.pydevproject
33
.DS_Store
44
.settings/
5+
.idea
56
.vscode/
7+
.idea
68

79
/_temp
810
brapi_blueprint.apib
911
brapi_openapi.yaml
1012
out.yaml
1113
Scripts/__pycache__/
1214
brapi_blueprint.apib.json
15+
/generator/.gradle/
16+
/generator/build

Scripts/buildERDData.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#! /usr/bin/env python
2+
3+
import yaml
4+
import sys
5+
import json
6+
import dereferenceAll
7+
import os
8+
9+
def buildERDDiagram(specPath):
10+
with open(specPath + '/brapi_openapi.yaml', "r") as stream:
11+
try:
12+
fullBrAPI = yaml.load(stream)
13+
stream.close()
14+
except yaml.YAMLError as exc:
15+
stream.close()
16+
17+
classes = []
18+
19+
if('components' in fullBrAPI):
20+
if('schemas' in fullBrAPI['components']):
21+
x = 0
22+
for schema in fullBrAPI['components']['schemas']:
23+
if('x-brapi-metadata' in fullBrAPI['components']['schemas'][schema] and fullBrAPI['components']['schemas'][schema]['x-brapi-metadata']['primaryModel']):
24+
schemaObj = fullBrAPI['components']['schemas'][schema]
25+
attributes = []
26+
if('properties' in schemaObj):
27+
attributes = list(schemaObj['properties'].keys())
28+
classObj = {'x': x, 'y':0, 'width': 150, 'classname': schema, 'attributes': attributes}
29+
x = x + 200
30+
classes.append(classObj)
31+
32+
return classes
33+
34+
def buildERDDiagramFile(specPath):
35+
fileContent = {}
36+
## For each BrAPI Module
37+
for module in next(os.walk(specPath))[1]:
38+
if module[:5] == 'BrAPI':
39+
print(specPath + module + '/')
40+
moduleObj = buildERDDiagram(specPath + module + '/')
41+
fileContent.update({module: moduleObj})
42+
43+
fileName = specPath + '/ERDDiagram.json'
44+
with open(fileName, 'w') as outfile:
45+
json.dump(fileContent, outfile, indent=4, sort_keys=True)
46+
print("fileName - " + fileName)
47+
48+
## Path to BrAPI spec repo. Should end in /Specification/
49+
rootPath = '.'
50+
if len(sys.argv) > 1 :
51+
rootPath = sys.argv[1];
52+
53+
if rootPath[-1:] != '/':
54+
rootPath = rootPath + '/'
55+
56+
buildERDDiagramFile(rootPath)

Scripts/buildJSONSchema2.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#! /usr/bin/env python
2+
3+
import yaml
4+
import glob
5+
import sys
6+
import json
7+
import re
8+
import os
9+
from copy import deepcopy
10+
import dereferenceAll
11+
12+
def buildJSONSchemas(parent, verbose):
13+
14+
if 'schemas' in parent['components']:
15+
for schemaName in parent['components']['schemas']:
16+
if verbose:
17+
print('Data model: ' + schemaName)
18+
title = schemaName
19+
schema = parent['components']['schemas'][schemaName]
20+
module = ''
21+
if 'x-brapi-metadata' in schema:
22+
if 'primaryModel' in schema['x-brapi-metadata'] and schema['x-brapi-metadata']['primaryModel']:
23+
if verbose:
24+
print('-- Added')
25+
if 'title' in schema['x-brapi-metadata']:
26+
title = schema['x-brapi-metadata']['title']
27+
if 'module' in schema['x-brapi-metadata']:
28+
module = schema['x-brapi-metadata']['module']
29+
buildJSONSchema(schema, title, module, verbose)
30+
31+
else:
32+
if verbose:
33+
print('-- Skipped')
34+
35+
else:
36+
print("Error: no schemas found")
37+
38+
39+
def buildJSONSchema(schema, title, module, verbose):
40+
schema = fixSchema(schema)
41+
schemaObj = {
42+
"$schema": "http://json-schema.org/draft/2020-12/schema",
43+
"$id": "https://brapi.org/Specification/BrAPI-Schema/" + module + "/" + title +".json",
44+
"$defs": {
45+
title : {
46+
"title": title,
47+
"type": "object",
48+
"properties": schema["properties"]
49+
}
50+
}
51+
}
52+
53+
if 'required' in schema:
54+
schemaObj['$defs'][title]['required'] = schema['required']
55+
56+
filename = outPath + module + '/' + title + '.json'
57+
os.makedirs(os.path.dirname(filename), exist_ok=True)
58+
with open(filename, 'w') as outfile:
59+
json.dump(schemaObj, outfile, indent=4, sort_keys=True)
60+
61+
62+
63+
def fixSchema(schema):
64+
schema = addMinItems(schema)
65+
schema = removeDeprecated(schema)
66+
schema = removeSwaggerTerms(schema)
67+
schema = fixStringFormats(schema)
68+
schema = allowNullFields(schema)
69+
# schema = addRequired(schema)
70+
schema = addRefs(schema)
71+
return schema
72+
73+
def addRefs(parent):
74+
newParent = deepcopy(parent)
75+
if type(parent) is dict:
76+
if 'properties' in newParent:
77+
className = ''
78+
if 'x-brapi-metadata' in newParent:
79+
className = newParent['x-brapi-metadata']['title']
80+
for term in parent['properties']:
81+
if term.endswith('DbId') and (term.casefold() != (className + 'DbId').casefold()):
82+
oldTermDetails = newParent['properties'].pop(term)
83+
newTerm = term[:-4]
84+
newTermTitle = newTerm[0].upper() + newTerm[1:]
85+
if newTerm + 'Name' in newParent['properties']:
86+
newParent['properties'].pop(newTerm + 'Name')
87+
newParent['properties'][newTerm] = { '$ref': newTermTitle + '.json#/$defs/' + newTermTitle, 'description': oldTermDetails['description'], 'relationshipType': 'EDITME-to-one', 'referencedAttribute': className[0].lower() + className[1:] + 's' }
88+
elif term.endswith('DbIds') and (term.casefold() != (className + 'DbIds').casefold()):
89+
oldTermDetails = newParent['properties'].pop(term)
90+
newTerm = term[:-5]
91+
newTermTitle = newTerm[0].upper() + newTerm[1:]
92+
newParent['properties'][newTerm + 's'] = { 'items':{ '$ref': newTermTitle + '.json#/$defs/' + newTermTitle }, 'type': 'array', 'description': oldTermDetails['description'], 'relationshipType': 'EDITME-to-many', 'referencedAttribute': className[0].lower() + className[1:] + 's' }
93+
94+
return newParent
95+
96+
def addMinItems(parent):
97+
newParent = deepcopy(parent)
98+
if 'data' in newParent['properties']:
99+
newParent['properties']['data']['minItems'] = 1
100+
return newParent
101+
102+
def removeDeprecated(parent):
103+
newParent = deepcopy(parent)
104+
if type(parent) is dict:
105+
for childKey in parent:
106+
child = parent[childKey]
107+
if type(child) is dict:
108+
if 'deprecated' in child and child['deprecated']:
109+
newParent.pop(childKey)
110+
else:
111+
newParent[childKey] = removeDeprecated(child)
112+
return newParent
113+
114+
def addRequired(parent):
115+
newParent = deepcopy(parent)
116+
if type(newParent) is dict:
117+
if 'properties' in newParent:
118+
if 'required' in parent:
119+
newParent['required'] = list(newParent['properties'].keys());
120+
for childKey in newParent:
121+
newParent[childKey] = addRequired(newParent[childKey])
122+
123+
return newParent
124+
125+
def removeSwaggerTerms(parent):
126+
newParent = deepcopy(parent)
127+
if type(newParent) is dict:
128+
if 'example' in newParent:
129+
newParent.pop('example')
130+
if 'discriminator' in newParent:
131+
newParent.pop('discriminator')
132+
for childKey in newParent:
133+
newParent[childKey] = removeSwaggerTerms(newParent[childKey])
134+
elif type(newParent) is list:
135+
newList = []
136+
for item in newParent:
137+
newList.append(removeSwaggerTerms(item))
138+
newParent = newList
139+
return newParent
140+
141+
def fixStringFormats(parent):
142+
newParent = deepcopy(parent)
143+
if type(parent) is dict:
144+
for childKey in parent:
145+
child = parent[childKey]
146+
if type(child) is dict:
147+
if 'format' in child and 'type' in child:
148+
if child['format'] != 'date-time' and child['format'] != 'uri':
149+
newParent[childKey].pop('format')
150+
else:
151+
newParent[childKey] = fixStringFormats(child)
152+
return newParent
153+
154+
def allowNullFields(parent):
155+
newParent = deepcopy(parent)
156+
if type(parent) is dict:
157+
if 'properties' in parent:
158+
159+
requiredFields = []
160+
if 'required' in parent:
161+
requiredFields = parent['required']
162+
163+
for fieldName in parent['properties']:
164+
fieldObj = parent['properties'][fieldName]
165+
newParent['properties'][fieldName] = allowNullFields(fieldObj)
166+
167+
if not fieldName in requiredFields:
168+
if 'enum' in fieldObj:
169+
newParent['properties'][fieldName]['enum'].append(None)
170+
types = ['null']
171+
if 'type' in fieldObj:
172+
types.append(fieldObj['type'])
173+
newParent['properties'][fieldName]['type'] = types
174+
else:
175+
for fieldName in parent:
176+
if type(parent[fieldName]) is dict:
177+
newParent[fieldName] = allowNullFields(parent[fieldName])
178+
179+
return newParent
180+
181+
182+
outPath = './out/'
183+
yamlPath = './brapi_openapi.yaml'
184+
verbose = False
185+
186+
verbose = '-v' in sys.argv
187+
188+
if '-brapi' in sys.argv:
189+
bi = sys.argv.index('-brapi')
190+
yamlPath = sys.argv[bi + 1]
191+
192+
if '-out' in sys.argv:
193+
ri = sys.argv.index('-out')
194+
outPath = sys.argv[ri + 1]
195+
196+
if outPath[-1] != '/':
197+
outPath = outPath + '/'
198+
199+
if verbose:
200+
print('input YAML file: ' + yamlPath)
201+
print('output directory: ' + outPath)
202+
203+
parentFile = dereferenceAll.dereferenceBrAPI(yamlPath, verbose)
204+
205+
buildJSONSchemas(parentFile, verbose)

Scripts/buildOpenAPI.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
import yaml
9+
from yaml import Loader, Dumper, load, dump
910
import glob
1011
import sys
1112
import os

Specification/BrAPI-Genotyping/VariantSets/Schemas/VariantSet.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ components:
1010
x-brapi-metadata:
1111
primaryModel: true
1212
module: BrAPI-Genotyping
13-
title: Variant
13+
title: VariantSet
1414
description: A VariantSet is a collection of variants and variant calls intended to be analyzed together.
1515
required:
1616
- variantSetDbId

Specification/BrAPI-Germplasm/Germplasm/Schemas/BreedingMethod.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ components:
3232
title: Breeding Method
3333
description: The techniques and protocol used to produce a Cross or Germplasm
3434

35+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$defs": {
3+
"AdditionalInfo": {
4+
"additionalProperties": {
5+
"type": "string"
6+
},
7+
"description": "A free space containing any additional information related to a particular object. A data source may provide any JSON object, unrestricted by the BrAPI specification.",
8+
"title": "AdditionalInfo",
9+
"type": "object"
10+
}
11+
},
12+
"$id": "https://brapi.org/Specification/BrAPI-Schema/Components/Common/AdditionalInfo.json",
13+
"$schema": "http://json-schema.org/draft/2020-12/schema"
14+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"$defs": {
3+
"Attribute": {
4+
"allOf": [
5+
{
6+
"$ref": "../BrAPI-Common/Variable.json#/$defs/Variable"
7+
},
8+
{
9+
"type": "object",
10+
"properties": {
11+
"attributeCategory": {
12+
"description": "General category for the attribute. very similar to Trait class.",
13+
"type": "string",
14+
"example": "Morphological"
15+
},
16+
"attributeDbId": {
17+
"description": "The ID which uniquely identifies this attribute within the given database server",
18+
"type": "string"
19+
},
20+
"attributeName": {
21+
"description": "A human readable name for this attribute",
22+
"type": "string",
23+
"example": "Plant Height 1"
24+
},
25+
"attributePUI": {
26+
"description": "The Permanent Unique Identifier of an Attribute, usually in the form of a URI",
27+
"type": "string",
28+
"example": "http://my-traits.com/trait/CO_123:0008012"
29+
},
30+
"attributeDescription": {
31+
"description": "A human readable description of this attribute",
32+
"type": "string",
33+
"example": "Height of the plant measured in meters by a tape"
34+
}
35+
},
36+
"required": [
37+
"attributeName"
38+
]
39+
}
40+
],
41+
"brapi-metadata": {
42+
"interface": true
43+
}
44+
}
45+
},
46+
"$id": "https://brapi.org/Specification/BrAPI-Schema/BrAPI-Phenotyping/Attribute.json",
47+
"$schema": "http://json-schema.org/draft/2020-12/schema"
48+
}

0 commit comments

Comments
 (0)