diff --git a/.gitignore b/.gitignore index c73e0c6..3409ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ pom.xml.asc package.json .idea/ output/ +bindings/js/**/*.js +bindings/js/**/*.js.map +bindings/js/node_modules \ No newline at end of file diff --git a/README.md b/README.md index 529751a..d19a9ca 100644 --- a/README.md +++ b/README.md @@ -124,9 +124,17 @@ $ lein jar $ lein npm install # this is only required on the first run $ lein node ``` - The output NPM package will be generated at `output/node`. +### Java Bindings + +The programming guide for the Java Bindings can be found [here](doc/java.md). +Javadoc for the bindings can be consulted [here](https://raml-org.github.io/api-modeling-framework/doc/java/apidocs/index.html). + +### JS Bindings + +The programming guide for the JS Bindings can be found [here](doc/js.md). +Documentation for the bindings can be consulted [here](https://raml-org.github.io/api-modeling-framework/doc/js/apidocs/index.html). ### API Modeling Framework Clojurescript/Web library diff --git a/bindings/java/amf-java.iml b/bindings/java/amf-java.iml new file mode 100644 index 0000000..d73eb00 --- /dev/null +++ b/bindings/java/amf-java.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bindings/java/pom.xml b/bindings/java/pom.xml new file mode 100644 index 0000000..6a1b60f --- /dev/null +++ b/bindings/java/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + org.raml + amf-java + jar + 0.1.2-SNAPSHOT + api-modeling-framework java bindings + API and domain modeling tools for RAML, OpenAPI (Swagger) and RDF + https://github.com/mulesoft-labs/api-modeling-framework + + + Apache-2.0 + http://www.eclipse.org/legal/epl-v10.html + + + + scm:git:git://github.com/raml-org/api-modelling-framework.git + scm:git:ssh://git@github.com/raml-org/api-modelling-framework.git + 61a8011ec87cd6a09be6bcdaa7c37de34c5a538c + + https://github.com/raml-org/api-modelling-framework + + + 1.7 + 1.7 + + + src + ../../target + ../../target/classes + + + + + + + + api-modeling-framework + api-modeling-framework + 0.1.2-SNAPSHOT + system + ${project.basedir}/../../target/api-modeling-framework-0.1.2-SNAPSHOT-standalone.jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + -Xdoclint:none + + + + + diff --git a/bindings/java/src/org/raml/amf/AMF.java b/bindings/java/src/org/raml/amf/AMF.java new file mode 100644 index 0000000..d29da76 --- /dev/null +++ b/bindings/java/src/org/raml/amf/AMF.java @@ -0,0 +1,66 @@ +package org.raml.amf; + +import org.raml.amf.generators.AMFJSONLDGenerator; +import org.raml.amf.generators.OpenAPIGenerator; +import org.raml.amf.generators.RAMLGenerator; +import org.raml.amf.parsers.AMFJSONLDParser; +import org.raml.amf.parsers.OpenAPIParser; +import org.raml.amf.parsers.RAMLParser; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Facade class providing access to the main IO facilities in the library + */ +public class AMF { + + /** + * Builds a RAML to AMF parser + * @return + */ + public static RAMLParser RAMLParser() { + return new RAMLParser(); + } + + /** + * Builds an OpenAPI to AMF parser + * @return + */ + public static OpenAPIParser OpenAPIParser() { + return new OpenAPIParser(); + } + + /** + * Builds a AMF encoded JSON-LD to AMF parser + * @return + */ + public static AMFJSONLDParser JSONLDParser() { + return new AMFJSONLDParser(); + } + + /** + * Builds a AMF to RAML generator + * @return + */ + public static RAMLGenerator RAMLGenerator() { + return new RAMLGenerator(); + } + + /** + * Builds a AMF to OpenAPI generator + * @return + */ + public static OpenAPIGenerator OpenAPIGenerator() { + return new OpenAPIGenerator(); + } + + /** + * Builds a AMF to JSON-LD generator + * @return + */ + public static AMFJSONLDGenerator JSONLDGenerator() { + return new AMFJSONLDGenerator(); + } +} diff --git a/bindings/java/src/org/raml/amf/core/Model.java b/bindings/java/src/org/raml/amf/core/Model.java new file mode 100644 index 0000000..cadfd57 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/Model.java @@ -0,0 +1,37 @@ +package org.raml.amf.core; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Base class for all AMF parsed models, provides methods to inspect and manipulate the model + */ +public abstract class Model { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + } + + protected Object rawModel; + + /** + * Builds the model from the inner Clojure data structure generated by the AMF library + * @param rawModel + */ + protected Model(Object rawModel) { + if (rawModel instanceof Exception) { + throw new InvalidModelException((Exception) rawModel); + } + this.rawModel = rawModel; + } + + /** + * Returns the raw Clojure data structure for this instance data + * @return + */ + public abstract Object clojureModel(); +} diff --git a/bindings/java/src/org/raml/amf/core/document/DeclaresDomainModel.java b/bindings/java/src/org/raml/amf/core/document/DeclaresDomainModel.java new file mode 100644 index 0000000..aa6d38d --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/DeclaresDomainModel.java @@ -0,0 +1,17 @@ +package org.raml.amf.core.document; + +import org.raml.amf.core.domain.DomainModel; + +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +public interface DeclaresDomainModel { + /** + * Declared DomainElements that can be re-used from other documents. + * @return List of domain elements. + */ + public List declares(); +} diff --git a/bindings/java/src/org/raml/amf/core/document/Document.java b/bindings/java/src/org/raml/amf/core/document/Document.java new file mode 100644 index 0000000..4a1fef3 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/Document.java @@ -0,0 +1,54 @@ +package org.raml.amf.core.document; + +import clojure.lang.IFn; +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * AMF Documents encode the main element of a description in a particular Domain Model + * For example, in RAML/HTTP, the main domain element is an APIDescription. + * + * Since AMF Documents encode Domain elements they behave like Fragments + * AMF Documents can also contains declarations of domain elements to be used in the description of the domain. + * From this point of view Documents also behave like Modules. + */ +public class Document extends DocumentModel implements EncodesDomainModel, DeclaresDomainModel { + public Document(Object rawModel) { + super(rawModel); + } + + /** + * Encoded domain element. It's considered to be the root element of a stand-alone description, not a domain element + * to be re-used and reference + * @return DomainElement encoded in the document. + * @throws InvalidModelException + */ + public DomainModel encodes() throws InvalidModelException { + IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "encodes"); + return DomainModel.fromRawModel(getFn.invoke(this.clojureModel())); + } + + /** + * List of domain elements declared in the document to be referenced in the encoded element. + * They are supposed to be private to the description and not meant to be re-used as in Modules. + * @return + */ + public List declares() { + IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "declares"); + List parsedElements = Clojure.toJavaList((List) getFn.invoke(this.clojureModel())); + ArrayList declared = new ArrayList<>(); + for(Object parsed : parsedElements) { + declared.add(DomainModel.fromRawModel(parsed)); + } + + return declared; + } +} diff --git a/bindings/java/src/org/raml/amf/core/document/DocumentModel.java b/bindings/java/src/org/raml/amf/core/document/DocumentModel.java new file mode 100644 index 0000000..f4f0d22 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/DocumentModel.java @@ -0,0 +1,187 @@ +package org.raml.amf.core.document; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +import api_modeling_framework.model.document.ParsedDocument; +import api_modeling_framework.model.document.ParsedFragment; +import api_modeling_framework.model.document.ParsedModule; +import clojure.lang.IFn; +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.core.Model; +import org.raml.amf.core.exceptions.ResolutionException; +import org.raml.amf.core.exceptions.UnknownModelReferenceException; +import org.raml.amf.utils.Clojure; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * AMF Document model that can be used to work with the graph of linked documents generated by the parser. + */ +public abstract class DocumentModel extends Model { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT); + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + DocumentModel(Object rawModel) throws InvalidModelException { + super(rawModel); + } + + private boolean resolved = false; + + /** + * Builds a new Model object for the referenced Document + * @param reference + * @return + * @throws MalformedURLException + * @throws InvalidModelException + * @throws UnknownModelReferenceException + */ + public DocumentModel modelForReference(URL reference) throws MalformedURLException, InvalidModelException, UnknownModelReferenceException { + IFn referenceModelFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "reference-model"); + URL[] refs = this.references(); + for(URL ref : refs) { + if (ref.sameFile(reference)) { + return DocumentModel.fromRawModel(referenceModelFn.invoke(this.rawModel, ref.toString().replace("file:",""))); + } + } + + throw new UnknownModelReferenceException(reference); + } + + + /** + * Returns the list document URIs referenced from the document that has been parsed to generate this model + * @return An array of URI locations for the remote documents + */ + public URL[] references() throws MalformedURLException { + IFn referencesFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "references"); + List references = (List) referencesFn.invoke(this.rawModel); + URL[] acc = new URL[references.size()]; + for (int i=0; i rawText() { + IFn rawFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "raw"); + String text = (String) rawFn.invoke(this.rawModel); + if (text != null) { + return Optional.of(text); + } else { + return Optional.empty(); + } + } + + + protected URL stringToURL(String location) throws MalformedURLException { + if (!location.contains("://")) { + return new URL("file://"+location); + } else { + return new URL(location); + } + } + + + /** + * Returns the native Clojure data structure for the model + * @return Clojure data structure encoding the model + */ + @Override + public Object clojureModel() { + IFn documentModelFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "document-model"); + return documentModelFn.invoke(this.rawModel); + } + + /** + * Factory method building the right wrapper Java DocumentModel subclass for the provided Clojure model data structure + * @param rawModel native Clojure encoded model + * @return The right DocumentModel + * @throws InvalidModelException + */ + public static DocumentModel fromRawModel(Object rawModel) throws InvalidModelException { + IFn unitKindFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "unit-kind"); + String unitKind = (String) unitKindFn.invoke(rawModel); + if (Objects.equals(unitKind, "module")) { + return new Module(rawModel); + } else if (Objects.equals(unitKind, "fragment")) { + return new Fragment(rawModel); + } else if (Objects.equals(unitKind, "document")) { + return new Document(rawModel); + } else { + throw new InvalidModelException(new Exception("Unknown type of document unit " + unitKind)); + } + } + + public DomainModel findDomainElement(String id) { + IFn findFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "find-element"); + Object res = findFn.invoke(this.rawModel, (isResolved() ? "domain": "document"), id); + if (res != null){ + Document doc = (Document) DocumentModel.fromRawModel(res); + return doc.encodes(); + } else { + return null; + } + } + + public boolean isResolved() { + return resolved; + } + + protected void setResolved(boolean resolved) { + this.resolved = resolved; + } +} diff --git a/bindings/java/src/org/raml/amf/core/document/EncodesDomainModel.java b/bindings/java/src/org/raml/amf/core/document/EncodesDomainModel.java new file mode 100644 index 0000000..f2e03d1 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/EncodesDomainModel.java @@ -0,0 +1,18 @@ +package org.raml.amf.core.document; + +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.core.exceptions.InvalidModelException; + +/** + * Created by antoniogarrote on 04/05/2017. + */ +public interface EncodesDomainModel { + + /** + * Encoded domain element described in the document element. + * @return DomainElement encoded in the document. + * @throws InvalidModelException + */ + public DomainModel encodes() throws InvalidModelException; + +} diff --git a/bindings/java/src/org/raml/amf/core/document/Fragment.java b/bindings/java/src/org/raml/amf/core/document/Fragment.java new file mode 100644 index 0000000..dea3431 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/Fragment.java @@ -0,0 +1,35 @@ +package org.raml.amf.core.document; + +import clojure.lang.IFn; +import clojure.lang.Keyword; +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * AMF Fragments encode a single DomainElement that can be referenced and re-used in other documents. + */ +public class Fragment extends DocumentModel implements EncodesDomainModel { + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT); + } + + public Fragment(Object rawModel) { + super(rawModel); + } + + /** + * Encoded Domain element that can referenced from other documents in the DocumentModel + * @return + * @throws InvalidModelException + */ + public DomainModel encodes() throws InvalidModelException { + IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "encodes"); + return DomainModel.fromRawModel(getFn.invoke(this.clojureModel())); + } +} diff --git a/bindings/java/src/org/raml/amf/core/document/Module.java b/bindings/java/src/org/raml/amf/core/document/Module.java new file mode 100644 index 0000000..135c00a --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/document/Module.java @@ -0,0 +1,37 @@ +package org.raml.amf.core.document; + +import clojure.lang.IFn; +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * AMF Modules contains collections of DomainElements that can be re-used and referenced from other documents in the + * Documentmodel. + */ +public class Module extends DocumentModel implements DeclaresDomainModel { + public Module(Object rawModel) { + super(rawModel); + } + + /** + * Declared DomainElements that can be re-used from other documents. + * @return List of domain elements. + */ + public List declares() { + IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "declares"); + List parsedElements = Clojure.toJavaList((List) getFn.invoke(this.clojureModel())); + ArrayList declared = new ArrayList<>(); + for(Object parsed : parsedElements) { + declared.add(DomainModel.fromRawModel(parsed)); + } + + return declared; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/APIDocumentation.java b/bindings/java/src/org/raml/amf/core/domain/APIDocumentation.java new file mode 100644 index 0000000..6a34b71 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/APIDocumentation.java @@ -0,0 +1,194 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedAPIDocumentation; +import clojure.lang.IFn; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Main EntryPoint of the description of HTTP RPC API + */ +public class APIDocumentation extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public APIDocumentation(ParsedAPIDocumentation rawModel) { + super(rawModel); + } + + /** + * Build a new empty API Documentation for the provided URI + * @param id + */ + public APIDocumentation(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedAPIDocumentation" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + public String getTermsOfService() { + return (String) this.wrapped().terms_of_service(); + } + + public void setTermsOfService(String termsOfService) { + this.rawModel = Clojure.setKw(this.rawModel, "terms-of-service", termsOfService); + } + + public String getBasePath() { + Object res = this.wrapped().base_path(); + if (res != null) + return (String) res; + else + return null; + } + + public void setBasePath(String basePath) { + this.rawModel = Clojure.setKw(this.rawModel, "base-path", basePath); + } + + public String getHost() { + Object res = this.wrapped().host(); + if (res != null) + return (String) res; + else + return null; + } + + public void setHost(String host) { + this.rawModel = Clojure.setKw(this.rawModel, "host", host); + } + + public String getVersion() { + Object res = this.wrapped().version(); + if (res != null) + return (String) res; + else + return null; + } + + public void setVersion(String host) { + this.rawModel = Clojure.setKw(this.rawModel, "version", host); + } + + /** + * URI Scheme for the paths in the API + * @return + */ + public String getScheme() { + Object res = this.wrapped().scheme(); + if (res != null) + return (String) res; + else + return null; + } + + public void setScheme(String scheme) { + this.rawModel = Clojure.setKw(this.rawModel, "scheme", scheme); + } + + public List getAccepts() { + return (List) this.wrapped().accepts(); + } + + public void setAccepts(List accepts) { + this.rawModel = Clojure.setKw(this.wrapped(), "accepts", Clojure.list(accepts)); + } + + public List getContentTypes() { + return (List) this.wrapped().content_type(); + } + + public void setContentTypes(List contentTypes) { + this.rawModel = Clojure.setKw(this.wrapped(), "content-type", Clojure.list(contentTypes)); + } + + /** + * List of HTTP headers in this unit + * @return + */ + public List
getHeaders() { + List headers = (List) this.wrapped().headers(); + List tmp = Clojure.toJavaList(headers); + ArrayList
eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedParameter x : tmp) { + Header parsed = new Header(x); + eps.add(parsed); + } + + return eps; + } + + public void setHeaders(List
headers) { + ArrayList raws = new ArrayList<>(); + for(Header x : headers) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "headers", Clojure.list(raws)); + } + + public List getParameters() { + List parameters = (List) this.wrapped().parameters(); + List tmp = Clojure.toJavaList(parameters); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedParameter x : tmp) { + Parameter parsed = new Parameter(x); + eps.add(parsed); + } + + return eps; + } + + public void setParameters(List parameters) { + ArrayList raws = new ArrayList<>(); + for(Parameter x : parameters) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "parameters", Clojure.list(raws)); + } + + /** + * List of EndPoints declared in this API + * @return + * @throws InvalidModelException + */ + public List getEndpoints() throws InvalidModelException { + List endpoints = (List) this.wrapped().endpoints(); + List tmp = Clojure.toJavaList(endpoints); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedEndPoint x : tmp) { + EndPoint parsed = new EndPoint(x); + eps.add(parsed); + } + + return eps; + } + + public void setEndPoints(List operations) { + ArrayList raws = new ArrayList<>(); + for(EndPoint x : operations) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "endpoints", Clojure.list(raws)); + } + + protected ParsedAPIDocumentation wrapped() { + return (ParsedAPIDocumentation) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/DomainModel.java b/bindings/java/src/org/raml/amf/core/domain/DomainModel.java new file mode 100644 index 0000000..97c6514 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/DomainModel.java @@ -0,0 +1,154 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.document.Node; +import api_modeling_framework.model.document.DocumentSourceMap; +import api_modeling_framework.model.domain.*; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.core.Model; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Base class for all Domain Model elements + */ +public class DomainModel extends Model { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public DomainModel(Object rawModel) { + super(rawModel); + } + + @Override + public Object clojureModel() { + return this.rawModel; + } + + public static DomainModel fromRawModel(Object rawModel) { + if (rawModel instanceof ParsedAPIDocumentation) { + return new APIDocumentation((ParsedAPIDocumentation) rawModel); + } else if (rawModel instanceof ParsedEndPoint) { + return new EndPoint((ParsedEndPoint) rawModel); + } else if (rawModel instanceof ParsedOperation) { + return new Operation((ParsedOperation) rawModel); + } else if (rawModel instanceof ParsedRequest) { + return new Request((ParsedRequest) rawModel); + } else if (rawModel instanceof ParsedResponse) { + return new Response((ParsedResponse) rawModel); + } else if (rawModel instanceof Payload) { + return new Payload((ParsedPayload) rawModel); + } else if (rawModel instanceof ParsedType) { + return new Type((ParsedType) rawModel); + } else { + throw new InvalidModelException(new Exception("Unknown DomainModel class " + rawModel)); + } + } + + /** + * Returns the unique URI identifier the Domain Element in the AMF graph + * @return + */ + public String getId() { + return (String) wrappedNode().id(); + } + + /** + * Returns a human readable string identifying the Domain Element + * @return + */ + public String getName() { + return (String) wrappedNode().name(); + } + + public void setName(String name) { + this.rawModel = Clojure.setKw(this.rawModel, "name", name); + } + + /** + * If true, the Domain Element is abstract and should be used to be extended by other Domain Elements + * @return + */ + public Boolean getAbstract() { + return (Boolean) Clojure.getKw(this.rawModel, "abstract") || false; + } + + public void setAbstract(Boolean abstractBool) { + this.rawModel = Clojure.setKw(this.rawModel, "abstract", abstractBool); + } + + public String getDescription() { + return (String) wrappedNode().name(); + } + + public void setDescription(String description) { + this.rawModel = Clojure.setKw(this.rawModel, "description", description); + } + + /** + * Optional list of source maps with lexical information associated to the Domain Element + * @return + */ + public List getSourceMaps() { + List operations = (List) this.wrappedNode().sources(); + List tmp = Clojure.toJavaList(operations); + ArrayList eps = new ArrayList<>(); + for(DocumentSourceMap x : tmp) { + SourceMap parsed = new SourceMap(x); + eps.add(parsed); + } + + return eps; + } + + public void setSourceMaps(List sourceMaps) { + ArrayList raws = new ArrayList<>(); + for(SourceMap x : sourceMaps) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "sources", Clojure.list(raws)); + } + + /** + * List of Domain Elements this element extends + * @return + */ + public List getExtends() { + List operations = (List) Clojure.getKw(this.rawModel, "extends"); + List tmp = Clojure.toJavaList(operations); + ArrayList eps = new ArrayList<>(); + for(Object x : tmp) { + eps.add(DomainModel.fromRawModel(x)); + } + + return eps; + } + + public void setExtends(List toExtend) { + ArrayList raws = new ArrayList<>(); + for(DomainModel x : toExtend) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "extends", Clojure.list(raws)); + } + + protected void setId(String id) { + this.rawModel = Clojure.setKw(this.rawModel, "id", id); + } + protected Node wrappedNode() { + return (Node) this.rawModel; + } + + public String toString() { + return (this.getId() + " :: " + super.toString()); + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/EndPoint.java b/bindings/java/src/org/raml/amf/core/domain/EndPoint.java new file mode 100644 index 0000000..08deb2a --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/EndPoint.java @@ -0,0 +1,82 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedEndPoint; +import api_modeling_framework.model.domain.ParsedOperation; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * EndPoints contains information about a HTTP remote location where a number of API operations have been bound + */ +public class EndPoint extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public EndPoint(ParsedEndPoint rawModel) { + super(rawModel); + } + + /** + * Builds a new EndPoint for the provided URI + * @param id + */ + public EndPoint(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedEndPoint" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + protected ParsedEndPoint wrapped() { + return (ParsedEndPoint) rawModel; + } + + /** + * Path for the URL where the operations of the EndPoint are bound + * @return + */ + public String getPath() { + return (String) this.wrapped().path(); + } + + public void setPath(String path) { + this.rawModel = Clojure.setKw(this.wrapped(), "path", path); + } + + /** + * List of API Operations bound to this EndPoint + * @return + */ + public List getSupportedOperations() { + List operations = (List) this.wrapped().supported_operations(); + List tmp = Clojure.toJavaList(operations); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedOperation x : tmp) { + Operation parsed = new Operation(x); + eps.add(parsed); + } + + return eps; + } + + public void setSupportedOperations(List operations) { + ArrayList raws = new ArrayList<>(); + for(Operation x : operations) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "supported-operations", Clojure.list(raws)); + } + +} diff --git a/bindings/java/src/org/raml/amf/core/domain/GenericOperationUnit.java b/bindings/java/src/org/raml/amf/core/domain/GenericOperationUnit.java new file mode 100644 index 0000000..d148bdc --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/GenericOperationUnit.java @@ -0,0 +1,86 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.HeadersHolder; +import api_modeling_framework.model.domain.PayloadHolder; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Base class for the Request and Response of an API + */ +public abstract class GenericOperationUnit extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public GenericOperationUnit(Object rawModel) { + super(rawModel); + } + + /** + * List of HTTP headers in this unit + * @return + */ + public List
getHeaders() { + List headers = (List) this.headersHolder().headers(); + List tmp = Clojure.toJavaList(headers); + ArrayList
eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedParameter x : tmp) { + Header parsed = new Header(x); + eps.add(parsed); + } + + return eps; + } + + public void setHeaders(List
headers) { + ArrayList raws = new ArrayList<>(); + for(Header x : headers) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "headers", Clojure.list(raws)); + } + + /** + * List of Payloads in the unit + * @return + */ + public List getPayloads() { + List payloads = (List) this.payloadHolder().payloads(); + List tmp = Clojure.toJavaList(payloads); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedPayload x : tmp) { + Payload parsed = new Payload(x); + eps.add(parsed); + } + + return eps; + } + + public void setPayloads(List payloads) { + ArrayList raws = new ArrayList<>(); + for(Payload x : payloads) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "payloads", Clojure.list(raws)); + } + + + private HeadersHolder headersHolder() { + return (HeadersHolder) this.rawModel; + } + + private PayloadHolder payloadHolder() { + return (PayloadHolder) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/GenericParameter.java b/bindings/java/src/org/raml/amf/core/domain/GenericParameter.java new file mode 100644 index 0000000..21b6969 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/GenericParameter.java @@ -0,0 +1,52 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedParameter; +import api_modeling_framework.model.domain.ParsedType; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Parameters include all kind of input or output information that is requested or returned by an API Operation that + * are not encoded in the Request or Response payloads. + * Parameters can be located in HTTP headers or in the domain, path or arguments of the request URL. + */ +public abstract class GenericParameter extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public GenericParameter(ParsedParameter rawModel) { + super(rawModel); + } + + public Boolean getRequired() { + return (Boolean) this.wrapped().required(); + } + + public void setRequired(Boolean required) { + this.rawModel = Clojure.setKw(this.rawModel, "required", required); + } + + public Type getSchema() { + ParsedType type = (ParsedType) this.wrapped().shape(); + return new Type(type); + } + + protected void setParameterKindInternal(String parameterKind) { + this.rawModel = Clojure.setKw(this.rawModel, "parameter-kind", parameterKind); + } + + public void setSchema(Type type) { + this.rawModel = Clojure.setKw(this.wrapped(), "shape", type.clojureModel()); + } + + protected ParsedParameter wrapped() { + return (ParsedParameter) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/GenericTag.java b/bindings/java/src/org/raml/amf/core/domain/GenericTag.java new file mode 100644 index 0000000..58378d5 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/GenericTag.java @@ -0,0 +1,61 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.document.Tag; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Tag included in a SourceMap + * Tags are tuples with an identifier, describing the kind of information in the mapping and an arbitrary value. + * Tags are also elements of the model, so they also have an associated URI. + */ +public class GenericTag extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public GenericTag(Object rawModel) { + super(rawModel); + } + + public GenericTag(String id, Object value) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, + "api-modeling-framework.model.document/->APITagTag" + ).invoke(id, value) + ); + } + + public GenericTag(String id, String tagId, Object value) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, + "api-modeling-framework.model.document/->APITagTag" + ).invoke(id, value) + ); + this.setTagId(tagId); + } + + public String getTagId() { + return (String) this.wrapped().tag_id(); + } + + public void setTagId(String tagId) { + this.rawModel = Clojure.setKw(this.rawModel, "tag-id", tagId); + } + + public Object getValue() { + return this.wrapped().value(); + } + + public void setValue(Object value) { + this.rawModel = Clojure.setKw(this.rawModel, "value", value); + } + + protected Tag wrapped() { + return (Tag) rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Header.java b/bindings/java/src/org/raml/amf/core/domain/Header.java new file mode 100644 index 0000000..db825b3 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Header.java @@ -0,0 +1,33 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedParameter; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Parameter representing a HTTP header + */ +public class Header extends GenericParameter { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Header(ParsedParameter rawModel) { + super(rawModel); + } + + public Header(String id) { + super((ParsedParameter) Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedParameter" + ).invoke(Clojure.map()) + ); + this.setId(id); + this.setParameterKindInternal("header"); + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Operation.java b/bindings/java/src/org/raml/amf/core/domain/Operation.java new file mode 100644 index 0000000..e09f36a --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Operation.java @@ -0,0 +1,144 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedOperation; +import api_modeling_framework.model.domain.ParsedRequest; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * A unit of business logic exposed by the API. Operations can be invoked using the associated HTTP method. + */ +public class Operation extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Operation(api_modeling_framework.model.domain.ParsedOperation rawModel) throws InvalidModelException { + super(rawModel); + } + + /** + * Builds a new empty Operation with the provide URI + * @param id + */ + public Operation(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedOperation" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + /** + * HTTP method that must be used to invoke the Operation + * @return + */ + public String getMethod() { + Object res = this.wrapped().method(); + if (res != null) { + return (String) res; + } else { + return null; + } + } + + public void setMethod(String method) { + this.rawModel = Clojure.setKw(this.wrapped(), "method", method); + } + + /** + * HTTP scheme that must be used to invoke the operation, overrides the default in APIDocumentation + * @return + */ + public String getScheme() { + Object res = this.wrapped().scheme(); + if (res != null) { + return (String) res; + } else { + return null; + } + } + + public void setScheme(String scheme) { + this.rawModel = Clojure.setKw(this.rawModel, "scheme", scheme); + } + + /** + * HTTP media-types accepted by the operation, overrides the default in APIDocumentation + */ + public List getAccepts() { + return (List) this.wrapped().accepts(); + } + + public void setAccepts(List accepts) { + this.rawModel = Clojure.setKw(this.wrapped(), "accepts", Clojure.list(accepts)); + } + + /** + * HTTP media-types returned by the operation, overrides the default in APIDocumentation + */ + public List getContentTypes() { + return (List) this.wrapped().content_type(); + } + + public void setContentTypes(List contentTypes) { + this.rawModel = Clojure.setKw(this.wrapped(), "content-type", Clojure.list(contentTypes)); + } + + /** + * List of responses for different HTTP status codes supported by this operation + * @return + */ + public List getResponses() { + List responses = (List) this.wrapped().responses(); + List tmp = Clojure.toJavaList(responses); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedResponse x : tmp) { + Response parsed = new Response(x); + eps.add(parsed); + } + + return eps; + } + + public void setResponses(List responses) { + ArrayList raws = new ArrayList<>(); + for(Operation x : responses) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.wrapped(), "responses", Clojure.list(raws)); + } + + /** + * Request information for the operation + * @return + */ + public Request getRequest() { + Object res = this.wrapped().request(); + if (res != null) { + ParsedRequest request = (ParsedRequest) res; + return new Request(request); + } else { + return null; + } + } + + public void setRequest(Request request) { + this.rawModel = Clojure.setKw(this.wrapped(), "request", request.clojureModel()); + } + + private ParsedOperation wrapped() { + return (ParsedOperation) this.rawModel; + } + +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Parameter.java b/bindings/java/src/org/raml/amf/core/domain/Parameter.java new file mode 100644 index 0000000..c7b7481 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Parameter.java @@ -0,0 +1,51 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedParameter; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * HTTP parameter other than a header. It can be located in the domain, path or query + */ +public class Parameter extends GenericParameter { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Parameter(ParsedParameter rawModel) { + super(rawModel); + } + + public Parameter(String id, String parameterKind) { + super((ParsedParameter) Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedParameter" + ).invoke(Clojure.map()) + ); + this.setId(id); + this.setParameterKindInternal(parameterKind); + } + + protected void setParameterKind(String parameterKind) { + setParameterKindInternal(parameterKind); + } + + public String getParameterKind() { + Object res = this.wrapped().parameter_kind(); + if (res != null) { + return (String) res; + } else { + return null; + } + } + + + protected ParsedParameter wrapped() { + return (ParsedParameter) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Payload.java b/bindings/java/src/org/raml/amf/core/domain/Payload.java new file mode 100644 index 0000000..4e840c2 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Payload.java @@ -0,0 +1,58 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedPayload; +import api_modeling_framework.model.domain.ParsedType; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Schema information for a Payload associated to a particular media-type + */ +public class Payload extends DomainModel { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Payload(ParsedPayload rawModel) { + super(rawModel); + } + + public Payload(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedPayload" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + public String getMediaType() { + return (String) this.wrapped().media_type(); + } + + public void setMediaType(String mediaType) { + this.rawModel = Clojure.setKw(this.rawModel, "media-type", mediaType); + } + + /** + * Schema information for the payload + * @return + */ + public Type getSchema() { + ParsedType type = (ParsedType) this.wrapped().schema(); + return new Type(type); + } + + public void setSchema(Type type) { + this.rawModel = Clojure.setKw(this.wrapped(), "schema", type.clojureModel()); + } + + private ParsedPayload wrapped() { + return (ParsedPayload) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Request.java b/bindings/java/src/org/raml/amf/core/domain/Request.java new file mode 100644 index 0000000..287ffa6 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Request.java @@ -0,0 +1,61 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedRequest; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * HTTP request information reuqired by an API Operation + */ +public class Request extends GenericOperationUnit { + + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Request(ParsedRequest request) throws InvalidModelException { + super(request); + } + + public Request(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedRequest" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + public List getParameters() { + List parameters = (List) this.wrapped().parameters(); + List tmp = Clojure.toJavaList(parameters); + ArrayList eps = new ArrayList<>(); + for(api_modeling_framework.model.domain.ParsedParameter x : tmp) { + Parameter parsed = new Parameter(x); + eps.add(parsed); + } + + return eps; + } + + public void setParameters(List parameters) { + ArrayList raws = new ArrayList<>(); + for(Parameter x : parameters) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "parameters", Clojure.list(raws)); + } + + private ParsedRequest wrapped() { + return (ParsedRequest) this.rawModel; + } + +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Response.java b/bindings/java/src/org/raml/amf/core/domain/Response.java new file mode 100644 index 0000000..b819aa9 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Response.java @@ -0,0 +1,52 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedResponse; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Information about the response returned by an operation, associated to a particular status + */ +public class Response extends GenericOperationUnit { + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Response(ParsedResponse rawModel) { + super(rawModel); + } + + public Response(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedResponse" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + /** + * HTTP status code for the response + * @return + */ + public String getStatusCode() { + String code = (String) this.wrapped().status_code(); + if (code != null) + return code; + else + return null; + } + + public void setStatusCode(String status) { + this.rawModel = Clojure.setKw(this.wrapped(), "status-code", status); + } + + private ParsedResponse wrapped() { + return (ParsedResponse) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/domain/SourceMap.java b/bindings/java/src/org/raml/amf/core/domain/SourceMap.java new file mode 100644 index 0000000..3caffb1 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/SourceMap.java @@ -0,0 +1,62 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.document.DocumentSourceMap; +import api_modeling_framework.model.document.Tag; +import org.raml.amf.utils.Clojure; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Lexical information generated by the parser. SourceMaps are optional and can + * be used to reconstruct the original lexical structure of the model during generation + */ +public class SourceMap extends DomainModel { + + public SourceMap(DocumentSourceMap rawModel) { + super(rawModel); + } + + protected DocumentSourceMap wrapped() { + return (DocumentSourceMap) this.rawModel; + } + + /** + * Each unit of lexical information stored as a tag + * @return + */ + public String getSource() { + return (String) this.wrapped().source(); + } + + public void setSource(String source) { + this.rawModel = Clojure.setKw(this.rawModel, "source", source); + } + + public List getTags() { + List operations = (List) this.wrapped().tags(); + List tmp = Clojure.toJavaList(operations); + ArrayList eps = new ArrayList<>(); + for(Tag x : tmp) { + GenericTag parsed = new GenericTag(x); + eps.add(parsed); + } + + return eps; + } + + public void setTags(List tags) { + ArrayList raws = new ArrayList<>(); + for(GenericTag x : tags) { + raws.add(x.clojureModel()); + } + + this.rawModel = Clojure.setKw(this.rawModel, "tags", Clojure.list(raws)); + } + +} diff --git a/bindings/java/src/org/raml/amf/core/domain/Type.java b/bindings/java/src/org/raml/amf/core/domain/Type.java new file mode 100644 index 0000000..3ad54c6 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/domain/Type.java @@ -0,0 +1,54 @@ +package org.raml.amf.core.domain; + +import api_modeling_framework.model.domain.ParsedType; +import clojure.lang.IFn; +import org.raml.amf.utils.Clojure; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Data Shape that describing a set of constraints over an operation unit payload + */ +public class Type extends DomainModel { + static { + Clojure.require(Clojure.CHESHIRE_CORE); + Clojure.require(Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN); + } + + public Type(Object rawModel) { + super(rawModel); + } + + public Type(String id) { + super(Clojure.var( + Clojure.API_MODELING_FRAMEWORK_MODEL_DOMAIN, + "api-modeling-framework.model.domain/map->ParsedType" + ).invoke(Clojure.map()) + ); + this.setId(id); + } + + /** + * JSON-LD string containing a SHACL shape that can be used to validate payloads for this operation unit + * @return + */ + public String getShape() { + IFn generateStringFn = Clojure.var(Clojure.CHESHIRE_CORE, "generate-string"); + return (String) generateStringFn.invoke(this.wrapped().shape()); + } + + /** + * Sets the SHACL shape for the payloads of this operation unit + * @param shaclShape valid SHACL shape encoded as JSON-LD string + */ + public void setShape(String shaclShape) { + IFn parseStringFn = Clojure.var(Clojure.CHESHIRE_CORE, "parse-string"); + this.rawModel = Clojure.setKw(this.rawModel, "shape", parseStringFn.invoke(shaclShape)); + } + + public ParsedType wrapped() { + return (ParsedType) this.rawModel; + } +} diff --git a/bindings/java/src/org/raml/amf/core/exceptions/InvalidModelException.java b/bindings/java/src/org/raml/amf/core/exceptions/InvalidModelException.java new file mode 100644 index 0000000..da99ed7 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/exceptions/InvalidModelException.java @@ -0,0 +1,15 @@ +package org.raml.amf.core.exceptions; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Exception related to a native clojure model that is not matching the expected value + */ +public class InvalidModelException extends RuntimeException { + + public InvalidModelException(Exception ex) { + super(ex); + } +} diff --git a/bindings/java/src/org/raml/amf/core/exceptions/ResolutionException.java b/bindings/java/src/org/raml/amf/core/exceptions/ResolutionException.java new file mode 100644 index 0000000..e54e660 --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/exceptions/ResolutionException.java @@ -0,0 +1,19 @@ +package org.raml.amf.core.exceptions; + +import org.raml.amf.core.document.DocumentModel; + +/** + * Created by antoniogarrote on 04/05/2017. + */ +public class ResolutionException extends Exception { + private final DocumentModel model; + + public ResolutionException(DocumentModel model, Exception ex) { + super("Error resolving model", ex); + this.model = model; + } + + public DocumentModel getModel() { + return model; + } +} diff --git a/bindings/java/src/org/raml/amf/core/exceptions/UnknownModelReferenceException.java b/bindings/java/src/org/raml/amf/core/exceptions/UnknownModelReferenceException.java new file mode 100644 index 0000000..941c6de --- /dev/null +++ b/bindings/java/src/org/raml/amf/core/exceptions/UnknownModelReferenceException.java @@ -0,0 +1,23 @@ +package org.raml.amf.core.exceptions; + +import java.net.URL; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Exception due to a reference to an unknown model + */ +public class UnknownModelReferenceException extends Exception { + private URL uknownReference; + + public UnknownModelReferenceException(URL reference) { + super("Cannot find model with reference " + reference.toString()); + this.uknownReference = reference; + } + + public URL getUknownReference() { + return uknownReference; + } +} diff --git a/bindings/java/src/org/raml/amf/examples/BasicParsingAndNavigation.java b/bindings/java/src/org/raml/amf/examples/BasicParsingAndNavigation.java new file mode 100644 index 0000000..338ce92 --- /dev/null +++ b/bindings/java/src/org/raml/amf/examples/BasicParsingAndNavigation.java @@ -0,0 +1,110 @@ +package org.raml.amf.examples; + +import org.raml.amf.AMF; +import org.raml.amf.core.document.Document; +import org.raml.amf.core.document.DocumentModel; +import org.raml.amf.core.document.EncodesDomainModel; +import org.raml.amf.core.document.Module; +import org.raml.amf.core.domain.APIDocumentation; +import org.raml.amf.core.domain.DomainModel; +import org.raml.amf.core.domain.EndPoint; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.core.exceptions.ResolutionException; +import org.raml.amf.core.exceptions.UnknownModelReferenceException; +import org.raml.amf.generators.*; +import org.raml.amf.parsers.ParsingException; +import org.raml.amf.parsers.ParsingOptions; +import org.raml.amf.parsers.RAMLParser; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ +public class BasicParsingAndNavigation { + + public static void main(String[] args) throws MalformedURLException, ParsingException, InvalidModelException, UnknownModelReferenceException, GenerationException, ResolutionException { + + URL toParse = new URL("file:///Users/antoniogarrote/Development/api-modelling-framework/resources/other-examples/world-music-api/api.raml"); + // Model model = new RAMLParser().parseFile(); + HashMap cacheDirs = new HashMap<>(); + cacheDirs.put("http://test.com/something","/Users/antoniogarrote/Development/api-modelling-framework/resources/other-examples/world-music-api"); + ParsingOptions options = new ParsingOptions().setCacheDirs(cacheDirs); + Document model = (Document) AMF.RAMLParser().parseFile(new URL("http://test.com/something/api.raml"), options); + System.out.println("GOT A MODEL"); + System.out.println(model); + + System.out.println("LOCATION: " + model.location()); + for (URL ref : model.references()) { + System.out.println("REFERENCE: " + ref); + } + + + toParse = new URL("file:///Users/antoniogarrote/Development/api-modelling-framework/resources/other-examples/world-music-api/api.raml"); + model = (Document) new RAMLParser().parseFile(toParse); + System.out.println("GOT A MODEL"); + System.out.println(model); + + DocumentModel resolvedModel = model.resolve(); + System.out.println("RESOLVED"); + System.out.println(AMF.RAMLGenerator().generateString(resolvedModel)); + + System.out.println("LOCATION: " + model.location()); + for (URL ref : model.references()) { + System.out.println("REFERENCE: " + ref); + } + + System.out.println(model.rawText().get()); + + URL targetRef = model.references()[1]; + System.out.println("TARGETTING " + targetRef); + DocumentModel targetModel = model.modelForReference(targetRef); + System.out.println("TARGET LOCATION: " + targetModel.location()); + System.out.println("TARGET MODEL CLASS " + targetModel.getClass()); + List declarations = ((Module) targetModel).declares(); + System.out.println("DECLARATIONS:"); + for(DomainModel decl : declarations) { + System.out.println(decl); + } + + Object foundModel = targetModel.findDomainElement("/Users/antoniogarrote/Development/api-modelling-framework/resources/other-examples/world-music-api/libraries/api.lib.raml#/definitions/Cat"); + System.out.println("FOUND?"); + System.out.println(foundModel); + + APIDocumentation api = (APIDocumentation) model.encodes(); + + System.out.println("API DOCUMENTATION " + api.getName()); + + System.out.println("ENDPOINTS " + api.getEndpoints()); + for (EndPoint endpoint : api.getEndpoints()) { + endpoint.setName("Modified " + endpoint.getName()); + System.out.println(endpoint.getName()); + } + + String generated = AMF.OpenAPIGenerator().generateString(targetModel); + System.out.println("GENERATED OpenAPI"); + System.out.println(generated); + + generated = AMF.RAMLGenerator().generateString( + new File("world_music.raml"), + targetModel + ); + System.out.println("GENERATED RAML"); + System.out.println(generated); + + generated = AMF.JSONLDGenerator().generateString( + new File("world_music.jsonld"), + targetModel, + new GenerationOptions() + .setFullgraph(true) + .setSourceMapGeneration(true) + ); + System.out.println("GENERATED JSONLD"); + System.out.println(generated); + } +} diff --git a/bindings/java/src/org/raml/amf/generators/AMFJSONLDGenerator.java b/bindings/java/src/org/raml/amf/generators/AMFJSONLDGenerator.java new file mode 100644 index 0000000..6f0b0ff --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/AMFJSONLDGenerator.java @@ -0,0 +1,16 @@ +package org.raml.amf.generators; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Generator that exports the AMF model as a JSON-LD document + */ +public class AMFJSONLDGenerator extends BaseGenerator { + + @Override + protected String generatorConstructor() { + return "->APIModelGenerator"; + } +} diff --git a/bindings/java/src/org/raml/amf/generators/BaseGenerator.java b/bindings/java/src/org/raml/amf/generators/BaseGenerator.java new file mode 100644 index 0000000..2592b05 --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/BaseGenerator.java @@ -0,0 +1,103 @@ +package org.raml.amf.generators; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +import clojure.lang.IFn; +import org.raml.amf.core.document.DocumentModel; +import org.raml.amf.utils.Clojure; + +import java.io.File; +import java.net.MalformedURLException; + +/** + * Basic interface for all AMF generators. It allows to generate syntax files out of AMF Document Models. + */ +public abstract class BaseGenerator { + static { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + } + + /** + * Serialises the model and stores it in the provided file path and options + * @param path Path where the model will be serialised + * @param model DocumentModel to be serialised + * @param options Generation options + */ + public void generateFile(File path, DocumentModel model, GenerationOptions options) throws GenerationException { + String location = path.getAbsolutePath().toString(); + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, generatorConstructor()); + Object generator = parserFn.invoke(); + IFn parseFileSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "generate-file-sync"); + try { + Object result = parseFileSync.invoke(generator, location, model.clojureModel(), options.build()); + if (result instanceof Exception) { + throw new GenerationException((Exception) result); + } + + } catch (RuntimeException e) { + throw new GenerationException(e); + } + } + + /** + * Serialises the model and stores it in the provided file path. + * @param path + * @param model + * @throws GenerationException + */ + public void generateFile(File path, DocumentModel model) throws GenerationException { + generateFile(path, model, new GenerationOptions()); + } + + /** + * Serialises the model and uses the provided file path as the default model location, applying the provided options + * @param path Path where the model will be serialised + * @param model DocumentModel to be serialised + */ + public String generateString(File path, DocumentModel model, GenerationOptions options) throws GenerationException { + return generateStringInternal(path.getAbsolutePath().toString(), model, options); + } + + /** + * Serialises the model and stores it in the using the privded file path as the model location + * @param path + * @param model + * @throws GenerationException + */ + public String generateString(File path, DocumentModel model) throws GenerationException { + return generateString(path, model, new GenerationOptions()); + } + + /** + * Serialises the model using the default location stored in the model + * @param model + * @throws GenerationException + */ + public String generateString(DocumentModel model) throws GenerationException { + try { + return generateStringInternal(model.location().toString(), model, new GenerationOptions()); + } catch (MalformedURLException ex) { + throw new GenerationException(ex); + } + } + + protected String generateStringInternal(String location, DocumentModel model, GenerationOptions options) throws GenerationException { + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, generatorConstructor()); + Object generator = parserFn.invoke(); + IFn generateStringSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "generate-string-sync"); + try { + Object result = generateStringSync.invoke(generator, location, model.clojureModel(), options.build()); + if (result instanceof Exception) { + throw new GenerationException((Exception) result); + } else { + return (String) result; + } + + } catch (RuntimeException e) { + throw new GenerationException(e); + } + } + protected abstract String generatorConstructor(); +} diff --git a/bindings/java/src/org/raml/amf/generators/GenerationException.java b/bindings/java/src/org/raml/amf/generators/GenerationException.java new file mode 100644 index 0000000..3817d26 --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/GenerationException.java @@ -0,0 +1,11 @@ +package org.raml.amf.generators; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +public class GenerationException extends Throwable { + public GenerationException(Exception rawModel) { + super(rawModel); + } +} diff --git a/bindings/java/src/org/raml/amf/generators/GenerationOptions.java b/bindings/java/src/org/raml/amf/generators/GenerationOptions.java new file mode 100644 index 0000000..440e64c --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/GenerationOptions.java @@ -0,0 +1,47 @@ +package org.raml.amf.generators; + +import clojure.lang.IPersistentMap; +import clojure.lang.PersistentHashMap; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +public class GenerationOptions { + + private Boolean generateSourceMaps; + private Boolean generateFullGraph; + + /** + * When serialising to JSON-LD, enables or disables the generation of source-maps + * @param shouldGenerate + */ + public GenerationOptions setSourceMapGeneration(Boolean shouldGenerate) { + this.generateSourceMaps = shouldGenerate; + return this; + } + + /** + * When serialising into JSON-LD, if set to true, all the JSON-LD RDF graph for referenced documents will be nested inside + * the JSON-LD document of the model. + * If set to false, only the URI will be serialised + * @param shouldGenerateFullGraph + */ + public GenerationOptions setFullgraph(Boolean shouldGenerateFullGraph) { + this.generateFullGraph = shouldGenerateFullGraph; + return this; + } + + public IPersistentMap build() { + IPersistentMap options = PersistentHashMap.EMPTY; + if (this.generateSourceMaps != null) { + options = options.assoc("source-maps?", this.generateSourceMaps); + } + if (this.generateFullGraph != null) { + options = options.assoc("full-graph?", this.generateSourceMaps); + } + + return options; + } +} diff --git a/bindings/java/src/org/raml/amf/generators/OpenAPIGenerator.java b/bindings/java/src/org/raml/amf/generators/OpenAPIGenerator.java new file mode 100644 index 0000000..f075933 --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/OpenAPIGenerator.java @@ -0,0 +1,17 @@ +package org.raml.amf.generators; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + + +/** + * Serialises the AMF model as an OpenAPI JSON document + */ +public class OpenAPIGenerator extends BaseGenerator { + + @Override + protected String generatorConstructor() { + return "->OpenAPIGenerator"; + } +} diff --git a/bindings/java/src/org/raml/amf/generators/RAMLGenerator.java b/bindings/java/src/org/raml/amf/generators/RAMLGenerator.java new file mode 100644 index 0000000..fa528e5 --- /dev/null +++ b/bindings/java/src/org/raml/amf/generators/RAMLGenerator.java @@ -0,0 +1,16 @@ +package org.raml.amf.generators; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Serialised the AMF model as a RAML YAML document + */ +public class RAMLGenerator extends BaseGenerator { + + @Override + protected String generatorConstructor() { + return "->RAMLGenerator"; + } +} diff --git a/bindings/java/src/org/raml/amf/parsers/AMFJSONLDParser.java b/bindings/java/src/org/raml/amf/parsers/AMFJSONLDParser.java new file mode 100644 index 0000000..c87d937 --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/AMFJSONLDParser.java @@ -0,0 +1,16 @@ +package org.raml.amf.parsers; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Wrapper class for the AMF OWL model parser, processes AMF model specification JSON-LD documents and generate the DocumentModel out of them + */ +public class AMFJSONLDParser extends BaseParser { + + @Override + protected String parserConstructor() { + return "->APIModelParser"; + } +} diff --git a/bindings/java/src/org/raml/amf/parsers/BaseParser.java b/bindings/java/src/org/raml/amf/parsers/BaseParser.java new file mode 100644 index 0000000..10ff909 --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/BaseParser.java @@ -0,0 +1,113 @@ +package org.raml.amf.parsers; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +import clojure.lang.IFn; +import org.raml.amf.core.document.DocumentModel; +import org.raml.amf.core.exceptions.InvalidModelException; +import org.raml.amf.utils.Clojure; + +import java.net.URL; + +/** + * Basic interface for all AMF parsers. It allows to parse syntax files and syntax text and generate the AMF Model out + * of it. + */ +public abstract class BaseParser { + + /** + * Generates a model parsing the file referenced by the provided URL. + * @param url Local or remote URL + * @return The parsed model + * @throws ParsingException + */ + public DocumentModel parseFile(URL url) throws ParsingException, InvalidModelException { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, parserConstructor()); + Object parser = parserFn.invoke(); + IFn parseFileSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "parse-file-sync"); + try { + Object rawModel = parseFileSync.invoke(parser, url.toString().replace("file:","")); + if (rawModel instanceof Exception) { + throw new ParsingException((Exception) rawModel); + } + return DocumentModel.fromRawModel(rawModel); + } catch (RuntimeException e) { + throw new ParsingException(e); + } + } + + /** + * Generates a model parsing the file referenced by the provided URL and parsing options. + * @param url Local or remote URL + * @param options Parsing options + * @return The parsed model + * @throws ParsingException + */ + public DocumentModel parseFile(URL url, ParsingOptions options) throws ParsingException, InvalidModelException { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, parserConstructor()); + Object parser = parserFn.invoke(); + IFn parseFileSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "parse-file-sync"); + try { + Object rawModel = parseFileSync.invoke(parser, url.toString().replace("file:",""), options.build()); + if (rawModel instanceof Exception) { + throw new ParsingException((Exception) rawModel); + } + return DocumentModel.fromRawModel(rawModel); + } catch (RuntimeException e) { + throw new ParsingException(e); + } + } + + protected abstract String parserConstructor(); + + /** + * Generates a model parsing the provided textual input syntax. + * @param text Input syntax to parse + * @param url Base URL for the document being parsed. It will be the base URL for the inclusions in this file + * @return The parsed Model + * @throws ParsingException + */ + public DocumentModel parseString(String text, URL url) throws ParsingException { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, parserConstructor()); + Object parser = parserFn.invoke(); + IFn parseFileSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "parse-string-sync"); + try { + Object rawModel = parseFileSync.invoke(parser, url.toString().replace("file:",""), text); + if (rawModel instanceof Exception) { + throw new ParsingException((Exception) rawModel); + } + return DocumentModel.fromRawModel(rawModel); + } catch (RuntimeException e) { + throw new ParsingException(e); + } + } + + /** + * Generates a model parsing the provided textual input syntax and parsing options. + * @param text Input syntax to parse + * @param url Base URL for the document being parsed. It will be the base URL for the inclusions in this file + * @param options Parsing options + * @return The parsed Model + * @throws ParsingException + */ + public DocumentModel parseString(String text, URL url, ParsingOptions options) throws ParsingException { + Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE); + IFn parserFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, parserConstructor()); + Object parser = parserFn.invoke(); + IFn parseFileSync = Clojure.var(Clojure.API_MODELING_FRAMEWORK_CORE, "parse-string-sync"); + try { + Object rawModel = parseFileSync.invoke(parser, url.toString().replace("file:",""), text, options.build()); + if (rawModel instanceof Exception) { + throw new ParsingException((Exception) rawModel); + } + return DocumentModel.fromRawModel(rawModel); + } catch (RuntimeException e) { + throw new ParsingException(e); + } + } +} diff --git a/bindings/java/src/org/raml/amf/parsers/OpenAPIParser.java b/bindings/java/src/org/raml/amf/parsers/OpenAPIParser.java new file mode 100644 index 0000000..9cab6fe --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/OpenAPIParser.java @@ -0,0 +1,15 @@ +package org.raml.amf.parsers; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Wrapper class for the AMF OpenAPI parser, processes OpenAPI specification documents and generate the DocumentModel out of them + */ +public class OpenAPIParser extends BaseParser { + @Override + protected String parserConstructor() { + return "->OpenAPIParser"; + } +} diff --git a/bindings/java/src/org/raml/amf/parsers/ParsingException.java b/bindings/java/src/org/raml/amf/parsers/ParsingException.java new file mode 100644 index 0000000..7720854 --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/ParsingException.java @@ -0,0 +1,18 @@ +package org.raml.amf.parsers; + +import java.io.IOException; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Exception produced while parsing an input syntax for the AMF parser + */ +public class ParsingException extends IOException { + + public ParsingException(Exception ex) { + super(ex); + } + +} diff --git a/bindings/java/src/org/raml/amf/parsers/ParsingOptions.java b/bindings/java/src/org/raml/amf/parsers/ParsingOptions.java new file mode 100644 index 0000000..6f7c56e --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/ParsingOptions.java @@ -0,0 +1,38 @@ +package org.raml.amf.parsers; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +import clojure.lang.IPersistentMap; +import clojure.lang.PersistentHashMap; + +import java.util.HashMap; + +/** + * Parsing options for parsing + */ +public class ParsingOptions { + private IPersistentMap cacheDirs; + + /** + * Sets a mapping from URL prefixes for references to local directories to resolve references in the syntax + * @param uriToDirs + */ + public ParsingOptions setCacheDirs(HashMap uriToDirs) { + this.cacheDirs = PersistentHashMap.EMPTY; + for (String key : uriToDirs.keySet()) { + this.cacheDirs = this.cacheDirs.assoc(key, uriToDirs.get(key)); + } + return this; + } + + public IPersistentMap build() { + IPersistentMap options = PersistentHashMap.EMPTY; + if (this.cacheDirs != null) { + options = options.assoc("cacheDirs", this.cacheDirs); + } + + return options; + } +} diff --git a/bindings/java/src/org/raml/amf/parsers/RAMLParser.java b/bindings/java/src/org/raml/amf/parsers/RAMLParser.java new file mode 100644 index 0000000..473d9cb --- /dev/null +++ b/bindings/java/src/org/raml/amf/parsers/RAMLParser.java @@ -0,0 +1,16 @@ +package org.raml.amf.parsers; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Wrapper class for the AMF RAML parser, processes RAML specification documents and generate the DocumentModel out of them + */ +public class RAMLParser extends BaseParser { + + @Override + protected String parserConstructor() { + return "->RAMLParser"; + } +} diff --git a/bindings/java/src/org/raml/amf/utils/Clojure.java b/bindings/java/src/org/raml/amf/utils/Clojure.java new file mode 100644 index 0000000..c50b7ca --- /dev/null +++ b/bindings/java/src/org/raml/amf/utils/Clojure.java @@ -0,0 +1,83 @@ +package org.raml.amf.utils; + +import clojure.lang.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by antoniogarrote on 04/05/2017. + */ + +/** + * Clojure interop utilties. + * Many of this utilities and this particular approach to interop is because of a bug related to core.async preventing + * us to use aot compilation and the class that could have been generated in api-modeling-framework.core + */ +public class Clojure { + + public static final String CLOJURE_CORE = "clojure.core"; + public static final String API_MODELING_FRAMEWORK_CORE = "api-modeling-framework.core"; + public static final String API_MODELING_FRAMEWORK_MODEL_DOCUMENT = "api-modeling-framework.model.document"; + public static final String API_MODELING_FRAMEWORK_MODEL_DOMAIN = "api-modeling-framework.model.domain"; + public static final String CHESHIRE_CORE = "cheshire.core"; + public static final Var REQUIRE= RT.var("clojure.core", "require"); + + + public static Object require(String nsName) { + return REQUIRE.invoke(Symbol.intern(nsName)); + } + + /** + * Looks up a var by name in the given namespace. + * + * The var can subsequently be invoked if it is a function. + * @param nsName + * @param varName + * @return + */ + public static Var var(String nsName, String varName) { + return RT.var(nsName,varName); + } + + public static Keyword kw(String name) { + return Keyword.find(name); + } + + public static Object getKw(Object target, String name) { + IFn getFn= var(CLOJURE_CORE, "get"); + return getFn.invoke(target, kw(name)); + } + + public static Object setKw(Object target, String name, Object value) { + IFn setFn= var(CLOJURE_CORE, "assoc"); + return setFn.invoke(target, kw(name), value); + } + + public static IPersistentVector list(List list) { + IPersistentVector tmp = PersistentVector.EMPTY; + for(Object e : list) { + tmp = tmp.cons(e); + } + + return tmp; + } + + public static List toJavaList(List xs) { + ArrayList tmp = new ArrayList<>(); + IFn firstFn= var(CLOJURE_CORE, "first"); + IFn restFn= var(CLOJURE_CORE, "rest"); + IFn emptyFn= var(CLOJURE_CORE, "empty?"); + + while(! (Boolean) emptyFn.invoke(xs)) { + tmp.add((T) firstFn.invoke(xs)); + xs = (List) restFn.invoke(xs); + } + + return tmp; + } + + public static IPersistentMap map() { + return PersistentHashMap.EMPTY; + } +} diff --git a/bindings/js/browserify.sh b/bindings/js/browserify.sh new file mode 100755 index 0000000..ae054e1 --- /dev/null +++ b/bindings/js/browserify.sh @@ -0,0 +1 @@ +browserify index.js -s amf --ignore-missing > amf_bindings.js diff --git a/bindings/js/index.ts b/bindings/js/index.ts new file mode 100644 index 0000000..bfcb76f --- /dev/null +++ b/bindings/js/index.ts @@ -0,0 +1,51 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ +import {RAMLParser} from "./src/parsers/RAMLParser"; +import {OpenAPIParser} from "./src/parsers/OpenAPIParser"; +import {AMFJSONLDParser} from "./src/parsers/AMFJSONLDParser"; +import {RAMLGenerator} from "./src/generators/RAMLGenerator"; +import {OpenAPIGenerator} from "./src/generators/OpenAPIGenerator"; +import {AMFJSONDGenerator} from "./src/generators/AMFJSONLDGenerator"; +export type URL = string; + +/** + * Facade class providing access to the main IO facilities in the library + */ +export class AMF { + /** + * Builds a RAML to AMF parser + * @return + */ + public static RAMLParser = new RAMLParser(); + + /** + * Builds an OpenAPI to AMF parser + * @return + */ + public static OpenAPIParser = new OpenAPIParser(); + + /** + * Builds a AMF encoded JSON-LD to AMF parser + * @return + */ + public static JSONLDParser = new AMFJSONLDParser(); + + /** + * Builds a AMF to RAML generator + * @return + */ + public static RAMLGenerator = new RAMLGenerator(); + + /** + * Builds a AMF to OpenAPI generator + * @return + */ + public static OpenAPIGenerator = new OpenAPIGenerator(); + + /** + * Builds a AMF to JSON-LD generator + * @return + */ + public static JSONLDGenerator = new AMFJSONDGenerator(); +} \ No newline at end of file diff --git a/bindings/js/package.json b/bindings/js/package.json new file mode 100644 index 0000000..a05c97d --- /dev/null +++ b/bindings/js/package.json @@ -0,0 +1,24 @@ +{ + "name": "amf-js", + "version": "0.1.2", + "description": "API and domain modeling tools for RAML, OpenAPI (Swagger) and RDF", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "api-modeling-framework": "0.1.2" + }, + "devDependencies": { + "gulp": "^3.9.1", + "gulp-tsc": "^1.3.2", + "gulp-typedoc": "^2.0.2", + "gulp-typescript": "^3.1.6", + "gulp-typings": "^2.0.4", + "typedoc": "^0.6.0", + "typescript": "^2.3.2", + "typings": "^2.1.1" + } +} diff --git a/bindings/js/src/Clojure.ts b/bindings/js/src/Clojure.ts new file mode 100644 index 0000000..1fd4650 --- /dev/null +++ b/bindings/js/src/Clojure.ts @@ -0,0 +1,45 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +require("api-modeling-framework"); +const cljscore = global['cljs'].core; + +export class Clojure { + public static kw(s: string): any { + return cljscore.keyword(s); + } + + public static cljsToJs(o: any): any { + return cljscore.clj__GT_js(o); + } + + public static jsToCljs(o: any): any { + return cljscore.js__GT_clj(o); + } + + public static cljsMap(xs: any, f:(x: any) => T): T[] { + const acc: T[] = []; + while(! cljscore.empty_QMARK_(xs)) { + const first = cljscore.first(xs); + xs = cljscore.rest(xs); + acc.push(f(first)); + } + + return acc; + } + + public static toCljsSeq(xs: any[]): any { + let acc = cljscore.vec(); + xs.forEach(x => { + acc = cljscore.cons(x, acc); + }); + + return cljscore.reverse(acc); + } + + public static core = global['cljs'].core; + public static amf = global['api_modeling_framework'].core; + public static amf_document = global['api_modeling_framework'].model.document; + public static amf_domain = global['api_modeling_framework'].model.domain; +} \ No newline at end of file diff --git a/bindings/js/src/core/Model.ts b/bindings/js/src/core/Model.ts new file mode 100644 index 0000000..b800a7a --- /dev/null +++ b/bindings/js/src/core/Model.ts @@ -0,0 +1,35 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {Clojure} from "../Clojure"; + +/** + * Base class for all AMF parsed models, provides methods to inspect and manipulate the model + */ +export abstract class Model { + + /** + * Builds the model from the inner Clojure data structure generated by the AMF library + * @param rawModel + */ + constructor(protected rawModel: any) { + if (rawModel instanceof Error) { + throw rawModel; + } + } + + /** + * Returns the raw Clojure data structure for this instance data + * @return + */ + public abstract clojureModel(): any; + + /** + * Returns the raw Clojure data as a JSON friendly data structure + * @return + */ + public toJSON(): any { + return Clojure.cljsToJs(this.clojureModel()); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/document/DeclaresDomainModel.ts b/bindings/js/src/core/document/DeclaresDomainModel.ts new file mode 100644 index 0000000..ffc0008 --- /dev/null +++ b/bindings/js/src/core/document/DeclaresDomainModel.ts @@ -0,0 +1,13 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "../domain/DomainModel"; + +export interface DeclaresDomainModel { + /** + * Declared DomainElements that can be re-used from other documents. + * @return List of domain elements. + */ + declares(): DomainModel[]; +} \ No newline at end of file diff --git a/bindings/js/src/core/document/Document.ts b/bindings/js/src/core/document/Document.ts new file mode 100644 index 0000000..cf251b3 --- /dev/null +++ b/bindings/js/src/core/document/Document.ts @@ -0,0 +1,23 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DocumentModel} from "./DocumentModel"; +import {EncodesDomainModel} from "./EncodesDomainModel"; +import {DeclaresDomainModel} from "./DeclaresDomainModel"; + +/** + * AMF Documents encode the main element of a description in a particular Domain Model + * For example, in RAML/HTTP, the main domain element is an APIDescription. + * + * Since AMF Documents encode Domain elements they behave like Fragments + * AMF Documents can also contains declarations of domain elements to be used in the description of the domain. + * From this point of view Documents also behave like Modules. + */ +export class Document extends DocumentModel implements EncodesDomainModel, DeclaresDomainModel { + + constructor(protected rawModel: any) { + super(rawModel || {}); + } +} + diff --git a/bindings/js/src/core/document/DocumentModel.ts b/bindings/js/src/core/document/DocumentModel.ts new file mode 100644 index 0000000..93fb1a7 --- /dev/null +++ b/bindings/js/src/core/document/DocumentModel.ts @@ -0,0 +1,135 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ +import {Model} from "../Model"; + +/** + * AMF Document model that can be used to work with the graph of linked documents generated by the parser. + */ +export class DocumentModel extends Model { + + protected resolved = false; + + constructor(protected rawModel: any) { + super(rawModel); + } + + public isResolved(): boolean { + return this.resolved; + } + + protected setResolved(isResolved: boolean) { + this.resolved = isResolved; + } + + /** + * Builds a new Model object for the referenced Document + * @param reference + * @return + */ + public modelForReference(reference: URL): DocumentModel { + const foundRef = this.references() + .filter(ref => ref === reference) + .pop(); + + if (foundRef == null) { + throw new Error("Unknown reference " + reference); + } else { + return DocumentModel.fromRawModel(Clojure.amf.reference_model(this.rawModel, foundRef)); + } + } + + /** + * Returns the list document URIs referenced from the document that has been parsed to generate this model + * @return An array of URI locations for the remote documents + */ + public references(): URL[] { + return Clojure.amf.references(this.rawModel) as URL[]; + } + + /** + * Returns the file location for the document that has been parsed to generate this model + * @return URL with the location of the file + */ + public location(): URL { + return Clojure.amf.location(this.rawModel); + } + + public rawText(): undefined | string { + return Clojure.amf.raw(this.rawModel); + } + + /** + * Applies the resolution algorithm and returns a new Model contained the resolved DomainModel + * @return + */ + public resolve(): DocumentModel { + const resolvedRawModel = Clojure.amf.domain_model(this.rawModel); + const resolvedModel = Clojure.amf.to_model(resolvedRawModel); + const model = DocumentModel.fromRawModel(resolvedModel); + if (model != null) { + model.setResolved(true); + return model; + } else { + throw new Error("Error resolving model, null model computed"); + } + } + + /** + * Factory method building the right wrapper Java DocumentModel subclass for the provided Clojure model data structure + * @param rawModel native Clojure encoded model + * @return The right DocumentModel + */ + public static fromRawModel(rawModel: any): DocumentModel { + if (rawModel == null) { + throw new Error("Cannot build DocumentModel from null"); + } + const unitKind = Clojure.amf.unit_kind(rawModel); + if (unitKind === "document") { + return new Document(rawModel); + } else if (unitKind === "module") { + return new Module(rawModel); + } else if (unitKind === "fragment") { + return new Fragment(rawModel); + } else { + throw new Error("Unsupported type of unit"); + } + } + + /** + * Encoded domain element. It's considered to be the root element of a stand-alone description, not a domain element + * to be re-used and reference + * @return DomainElement encoded in the document. + */ + public encodes(): DomainModel { + const encodes = Clojure.amf_document.encodes(this.clojureModel()); + return DomainModel.fromRawModel(encodes); + } + + /** + * List of domain elements declared in the document to be referenced in the encoded element. + * They are supposed to be private to the description and not meant to be re-used as in Modules. + * @return + */ + public declares(): DomainModel[] { + return Clojure.cljsMap( + Clojure.amf_document.declares(this.clojureModel()), + dec => DomainModel.fromRawModel(dec) + ); + } + + /** + * Returns the native Clojure data structure for the model + * @return Clojure data structure encoding the model + */ + public clojureModel(): any { + return Clojure.amf.document_model(this.rawModel); + } + +} + +import {Document} from "./Document"; +import {Module} from "./Module"; +import {Fragment} from "./Fragment"; +import {DomainModel} from "../domain/DomainModel"; +import {Clojure} from "../../Clojure"; \ No newline at end of file diff --git a/bindings/js/src/core/document/EncodesDomainModel.ts b/bindings/js/src/core/document/EncodesDomainModel.ts new file mode 100644 index 0000000..39b028e --- /dev/null +++ b/bindings/js/src/core/document/EncodesDomainModel.ts @@ -0,0 +1,14 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "../domain/DomainModel"; + +export interface EncodesDomainModel { + /** + * Encoded domain element described in the document element. + * @return DomainElement encoded in the document. + * @throws InvalidModelException + */ + encodes(): DomainModel; +} \ No newline at end of file diff --git a/bindings/js/src/core/document/Fragment.ts b/bindings/js/src/core/document/Fragment.ts new file mode 100644 index 0000000..fac99fe --- /dev/null +++ b/bindings/js/src/core/document/Fragment.ts @@ -0,0 +1,24 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DocumentModel} from "./DocumentModel"; +import {EncodesDomainModel} from "./EncodesDomainModel"; +import {DomainModel} from "../domain/DomainModel"; + +/** + * AMF Fragments encode a single DomainElement that can be referenced and re-used in other documents. + */ +export class Fragment extends DocumentModel implements EncodesDomainModel { + constructor(protected rawModel: any) { + super(rawModel); + } + + declares(): DomainModel[] { + throw new Error("Fragments don't declare domain elements"); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/document/Module.ts b/bindings/js/src/core/document/Module.ts new file mode 100644 index 0000000..f2278e0 --- /dev/null +++ b/bindings/js/src/core/document/Module.ts @@ -0,0 +1,25 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DocumentModel} from "./DocumentModel"; +import {DeclaresDomainModel} from "./DeclaresDomainModel"; +import {DomainModel} from "../domain/DomainModel"; + +/** + * AMF Modules contains collections of DomainElements that can be re-used and referenced from other documents in the + * Documentmodel. + */ +export class Module extends DocumentModel implements DeclaresDomainModel{ + constructor(protected rawModel: any) { + super(rawModel); + } + + encodes(): DomainModel { + throw new Error("Modules don't encode domain elements"); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/APIDocumentation.ts b/bindings/js/src/core/domain/APIDocumentation.ts new file mode 100644 index 0000000..b6da72b --- /dev/null +++ b/bindings/js/src/core/domain/APIDocumentation.ts @@ -0,0 +1,126 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; +import {EndPoint} from "./EndPoint"; +import {Parameter} from "./Parameter"; +import {Header} from "./Header"; + +/** + * Main EntryPoint of the description of HTTP RPC API + */ +export class APIDocumentation extends DomainModel { + + public getTermsOfService(): string | undefined { + return Clojure.amf_domain.terms_of_service(this.rawModel); + } + + public setTermsOfService(termsOfService: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("terms-of-service"), termsOfService); + } + + public getBasePath(): string | undefined { + return Clojure.amf_domain.base_path(this.rawModel); + } + + public setBasePath(basePath: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("base-path"), basePath); + } + + public getHost(): string | undefined { + return Clojure.amf_domain.host(this.rawModel); + } + + public setHost(host: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("host"), host); + } + + public getVersion(): string | undefined { + return Clojure.amf_domain.version(this.rawModel); + } + + public setVersion(version: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("version")); + } + + /** + * URI Scheme for the paths in the API + * @return + */ + public getScheme(): string | undefined { + return Clojure.amf_domain.scheme(this.rawModel); + } + + public setScheme(scheme: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("scheme"), scheme); + } + + public getAccepts(): string[] { + return Clojure.cljsToJs(Clojure.amf_domain.accepts(this.rawModel)); + } + + public setAccepts(mediaTypes: string[]) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("accepts"), Clojure.jsToCljs(mediaTypes)); + } + + public getContentTypes(): string[] { + return Clojure.cljsToJs(Clojure.amf_domain.content_type(this.rawModel)); + } + + public setContentTypes(mediaTypes: string[]) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("content-type"), Clojure.jsToCljs(mediaTypes)); + } + + public getParameters(): Parameter[] { + const parametersCljs = Clojure.amf_domain.parameters(this.rawModel); + return Clojure.cljsMap(parametersCljs, (e) => new Parameter(e)); + } + + public setParameters(parameters: Parameter[]) { + const newParameters = Clojure.toCljsSeq(parameters.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("parameters"), newParameters); + } + + /** + * List of HTTP headers in this unit + * @return + */ + public getHeaders(): Header[] { + const headersCljs = Clojure.amf_domain.headers(this.rawModel); + return Clojure.cljsMap(headersCljs, (e) => new Header(e)); + } + + public setHeaders(headers: Header[]) { + const newHeaders = Clojure.toCljsSeq(headers.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("headers"), newHeaders); + } + + + /** + * List of EndPoints declared in this API + * @return + * @throws InvalidModelException + */ + public getEndPoints(): EndPoint[] { + const endpointsCljs = Clojure.amf_domain.endpoints(this.rawModel); + return Clojure.cljsMap(endpointsCljs, (e) => new EndPoint(e)); + } + + public setEndPoints(endpoints: EndPoint[]) { + const newEndpoints = Clojure.toCljsSeq(endpoints.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("endpoints"), newEndpoints); + } + + /** + * Build a new empty API Documentation for the provided URI + * @param id + */ + public static build(id: string): APIDocumentation { + return new APIDocumentation(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedAPIDocumentation, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/DomainModel.ts b/bindings/js/src/core/domain/DomainModel.ts new file mode 100644 index 0000000..58160bb --- /dev/null +++ b/bindings/js/src/core/domain/DomainModel.ts @@ -0,0 +1,117 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {Clojure} from "../../Clojure"; +import {Model} from "../Model"; + +/** + * Base class for all Domain Model elements + */ +export class DomainModel extends Model { + + constructor(protected rawModel: any) { + super(rawModel); + } + + /** + * Factory method building the right wrapper Java DomainModel subclass for the provided Domain Model Clojure data structure + * @param rawModel native Clojure encoded model + * @return The right DocumentModel + */ + public static fromRawModel(rawModel: any): DomainModel { + const type = Clojure.core.type(rawModel); + if (type === Clojure.amf_domain.ParsedAPIDocumentation) { + return new APIDocumentation(rawModel); + } else if (type === Clojure.amf_domain.ParsedEndPoint) { + return new EndPoint(rawModel); + } else if (type === Clojure.amf_domain.ParsedOperation) { + return new Operation(rawModel); + } else if (type === Clojure.amf_domain.ParsedRequest) { + return new Request(rawModel); + } else if (type === Clojure.amf_domain.ParsedResponse) { + return new Response(rawModel); + } else if (type === Clojure.amf_domain.ParsedPayload) { + return new Payload(rawModel); + } else if (type === Clojure.amf_domain.ParsedType) { + return new Type(rawModel); + } else { + throw new Error(`Unknown model ${type}`) + } + } + + public clojureModel(): any { + return this.rawModel; + } + + /** + * Returns the unique URI identifier the Domain Element in the AMF graph + * @return + */ + public getId(): string { + return Clojure.amf_document.id(this.rawModel); + } + + /** + * Returns a human readable string identifying the Domain Element + * @return + */ + public getName(): string | undefined { + return Clojure.amf_domain.name(this.rawModel); + } + + public setName(name: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("name"), name); + } + + /** + * If true, the Domain Element is abstract and should be used to be extended by other Domain Elements + * @return + */ + public getAbstract(): boolean | undefined { + return Clojure.amf_domain.abstract(this.rawModel); + } + + public setAbstract(abstract: boolean | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("abstract"), abstract); + } + + /** + * List of Domain Elements this element extends + * @return + */ + public getExtends(): DomainModel[] { + let extensions = Clojure.amf_domain.extends(this.rawModel); + return Clojure.cljsMap(extensions, (e) => DomainModel.fromRawModel(e)); + } + + public setExtensions(extensions: DomainModel[]) { + const newExtensions = extensions.map(e => e.clojureModel()); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("extends"), Clojure.toCljsSeq(newExtensions)); + } + + /** + * Optional list of source maps with lexical information associated to the Domain Element + * @return + */ + public getSourceMaps(): SourceMap[] { + let sourceMaps = Clojure.amf_domain.sources(this.rawModel); + return Clojure.cljsMap(sourceMaps, (e) => new SourceMap(e)); + } + + public setSourceMaps(sourceMaps: SourceMap[]) { + const newSourceMaps = sourceMaps.map(e => e.clojureModel()); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("sources"), Clojure.toCljsSeq(newSourceMaps)); + } + + public static domain_builder = Clojure.amf.map__GT_JSDomainBuilder() +} + +import {APIDocumentation} from "./APIDocumentation"; +import {EndPoint} from "./EndPoint"; +import {Operation} from "./Operation";import { SourceMap } from "./SourceMap"; +import {Payload} from "./Payload"; +import {Parameter} from "./Parameter"; +import {Type} from "./Type"; +import {Request} from "./Request"; +import {Response} from "./Response"; diff --git a/bindings/js/src/core/domain/EndPoint.ts b/bindings/js/src/core/domain/EndPoint.ts new file mode 100644 index 0000000..3a959c2 --- /dev/null +++ b/bindings/js/src/core/domain/EndPoint.ts @@ -0,0 +1,49 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ +import {Clojure} from "../../Clojure"; +import {DomainModel} from "./DomainModel"; +import {Operation} from "./Operation"; + +/** + * EndPoints contains information about a HTTP remote location where a number of API operations have been bound + */ +export class EndPoint extends DomainModel { + + /** + * Path for the URL where the operations of the EndPoint are bound + * @return + */ + public getPath(): string | undefined{ + return Clojure.amf_domain.path(this.rawModel); + } + + public setPath(basePath: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("path"), basePath); + } + + /** + * List of API Operations bound to this EndPoint + * @return + */ + public getSupportedOperations(): Operation[] { + const endpointsCljs = Clojure.amf_domain.supported_operations(this.rawModel); + return Clojure.cljsMap(endpointsCljs, (e) => new Operation(e)); + } + + public setSupportedOperations(operations: Operation[]) { + const newOperations = Clojure.toCljsSeq(operations.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("supported-operations"), newOperations); + } + + /** + * Builds a new EndPoint for the provided URI + * @param id + */ + public static build(id: string): EndPoint { + return new EndPoint(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedEndPoint, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/GenericOperationUnit.ts b/bindings/js/src/core/domain/GenericOperationUnit.ts new file mode 100644 index 0000000..c83d62e --- /dev/null +++ b/bindings/js/src/core/domain/GenericOperationUnit.ts @@ -0,0 +1,43 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Header} from "./Header"; +import {Clojure} from "../../Clojure"; +import {Payload} from "./Payload"; + +/** + * Base class for the Request and Response of an API + */ +export abstract class GenericOperationUnit extends DomainModel { + + /** + * List of HTTP headers in this unit + * @return + */ + public getHeaders(): Header[] { + const headersCljs = Clojure.amf_domain.headers(this.rawModel); + return Clojure.cljsMap(headersCljs, (e) => new Header(e)); + } + + public setHeaders(headers: Header[]) { + const newHeaders = Clojure.toCljsSeq(headers.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("headers"), newHeaders); + } + + /** + * List of Payloads in the unit + * @return + */ + public getPayloads(): Payload[] { + const payloadsCljs = Clojure.amf_domain.payloads(this.rawModel); + return Clojure.cljsMap(payloadsCljs, (e) => new Payload(e)); + } + + public setPayloads(payloads: Payload[]) { + const newPayloads = Clojure.toCljsSeq(payloads.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("payloads"), newPayloads); + } + +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/GenericParameter.ts b/bindings/js/src/core/domain/GenericParameter.ts new file mode 100644 index 0000000..375463d --- /dev/null +++ b/bindings/js/src/core/domain/GenericParameter.ts @@ -0,0 +1,29 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; + +export type ParameterKind = "header" | "domain" | "path" | "query"; + +/** + * Parameters include all kind of input or output information that is requested or returned by an API Operation that + * are not encoded in the Request or Response payloads. + * Parameters can be located in HTTP headers or in the domain, path or arguments of the request URL. + */ +export abstract class GenericParameter extends DomainModel { + + public getRequired(): boolean { + return Clojure.amf_domain.host(this.rawModel) || false; + } + + public setRequired(required: boolean) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("required"), required); + } + + public setParameterKindInternal(parameterKind: ParameterKind) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("parameter-kind"), parameterKind); + } + +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/GenericTag.ts b/bindings/js/src/core/domain/GenericTag.ts new file mode 100644 index 0000000..5c4a09a --- /dev/null +++ b/bindings/js/src/core/domain/GenericTag.ts @@ -0,0 +1,42 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; + +/** + * Tag included in a SourceMap + * Tags are tuples with an identifier, describing the kind of information in the mapping and an arbitrary value. + * Tags are also elements of the model, so they also have an associated URI. + */ +export class GenericTag extends DomainModel { + + public getTagId(): string | undefined { + return Clojure.amf_domain.tagId(this.rawModel); + } + + public setTagId(tagId: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("tag-id"), tagId); + } + + public getValue(): string | undefined { + return Clojure.amf_domain.value(this.rawModel); + } + + public setValue(value: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("value"), value); + } + + + public static build(id: string, value: string, tagId?: string): GenericTag { + let tag = new GenericTag(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_document.map__GT_APITagTag, + id)); + tag.setValue(value); + if (tagId != null) + tag.setTagId(tagId); + return tag; + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Header.ts b/bindings/js/src/core/domain/Header.ts new file mode 100644 index 0000000..62405af --- /dev/null +++ b/bindings/js/src/core/domain/Header.ts @@ -0,0 +1,25 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {GenericParameter} from "./GenericParameter"; +import {Clojure} from "../../Clojure"; +import {DomainModel} from "./DomainModel"; + +/** + * Parameter representing a HTTP header + */ +export class Header extends GenericParameter { + + public constructor(rawModel: any) { + super(rawModel); + this.setParameterKindInternal("header"); + } + + public static build(id: string): Header { + return new Header(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedParameter, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Operation.ts b/bindings/js/src/core/domain/Operation.ts new file mode 100644 index 0000000..ec0c7ac --- /dev/null +++ b/bindings/js/src/core/domain/Operation.ts @@ -0,0 +1,99 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ +import {DomainModel} from "./DomainModel"; +import {Request} from "./Request"; +import {Response} from "./Response"; +import {Clojure} from "../../Clojure"; + +/** + * A unit of business logic exposed by the API. Operations can be invoked using the associated HTTP method. + */ +export class Operation extends DomainModel { + + /** + * HTTP method that must be used to invoke the Operation + * @return + */ + public getMethod(): string | undefined { + return Clojure.amf_domain.method(this.rawModel); + } + + public setMethod(method: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("method"), method); + } + + /** + * HTTP scheme that must be used to invoke the operation, overrides the default in APIDocumentation + * @return + */ + public getScheme(): string | undefined { + return Clojure.amf_domain.scheme(this.rawModel); + } + + public setScheme(scheme: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("scheme"), scheme); + } + + public getAccepts(): string[] { + return Clojure.cljsToJs(Clojure.amf_domain.accepts(this.rawModel)); + } + + /** + * HTTP media-types accepted by the operation, overrides the default in APIDocumentation + */ + public setAccepts(mediaTypes: string[]) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("accepts"), Clojure.jsToCljs(mediaTypes)); + } + + /** + * HTTP media-types returned by the operation, overrides the default in APIDocumentation + */ + public getContentTypes(): string[] { + return Clojure.cljsToJs(Clojure.amf_domain.content_type(this.rawModel)); + } + + public setContentTypes(mediaTypes: string[]) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("content-type"), Clojure.jsToCljs(mediaTypes)); + } + + /** + * Request information for the operation + * @return + */ + public getRequest(): Request | undefined { + let request = Clojure.amf_domain.request(this.rawModel); + if (request != null) { + return new Request(request); + } + } + + public setRequest(request: Request | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("request"), request != null ? request.clojureModel() : null); + } + + /** + * List of responses for different HTTP status codes supported by this operation + * @return + */ + public getResponses(): Response[] { + let responses = Clojure.amf_domain.responses(this.rawModel); + return Clojure.cljsMap(responses, (e) => new Response(e)); + } + + public setResponses(responses: Response[]) { + const newResponses = responses.map(e => e.clojureModel()); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("request"), Clojure.toCljsSeq(newResponses)); + } + + /** + * Builds a new empty Operation with the provide URI + * @param id + */ + public static build(id: string): Operation { + return new Operation(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedOperation, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Parameter.ts b/bindings/js/src/core/domain/Parameter.ts new file mode 100644 index 0000000..c17534d --- /dev/null +++ b/bindings/js/src/core/domain/Parameter.ts @@ -0,0 +1,20 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {Clojure} from "../../Clojure"; +import {DomainModel} from "./DomainModel"; +import {GenericParameter, ParameterKind} from "./GenericParameter"; + +/** + * HTTP parameter other than a header. It can be located in the domain, path or query + */ +export class Parameter extends GenericParameter { + + public static build(id: string, parameterKind: ParameterKind): Parameter { + return new Parameter(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedParameter, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Payload.ts b/bindings/js/src/core/domain/Payload.ts new file mode 100644 index 0000000..aad463a --- /dev/null +++ b/bindings/js/src/core/domain/Payload.ts @@ -0,0 +1,42 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; +import {Type} from "./Type"; + +/** + * Schema information for a Payload associated to a particular media-type + */ +export class Payload extends DomainModel { + + public getMediaType(): string | undefined { + return Clojure.amf_domain.media_type(this.rawModel); + } + + public setMediaType(mediaType: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("media-type"), mediaType); + } + + /** + * Schema information for the payload + * @return + */ + public getSchema(): Type | undefined { + const type = Clojure.amf_domain.schema(this.rawModel); + if (type != null) + return new Type(type); + } + + public setSchema(schema: Type | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("media-type"), schema != null ? schema.clojureModel() : null); + } + + public static build(id: string): Payload { + return new Payload(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedPayload, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Request.ts b/bindings/js/src/core/domain/Request.ts new file mode 100644 index 0000000..fbd4223 --- /dev/null +++ b/bindings/js/src/core/domain/Request.ts @@ -0,0 +1,32 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {GenericOperationUnit} from "./GenericOperationUnit"; +import {Clojure} from "../../Clojure"; +import {DomainModel} from "./DomainModel"; +import {Parameter} from "./Parameter"; + +/** + * HTTP request information reuqired by an API Operation + */ +export class Request extends GenericOperationUnit { + + public getParameters(): Parameter[] { + const parametersCljs = Clojure.amf_domain.parameters(this.rawModel); + return Clojure.cljsMap(parametersCljs, (e) => new Parameter(e)); + } + + public setParameters(parameters: Parameter[]) { + const newParameters = Clojure.toCljsSeq(parameters.map(x => x.clojureModel())); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("parameters"), newParameters); + } + + public static build(id: string): Request { + return new Request(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedRequest, + id)); + } + +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Response.ts b/bindings/js/src/core/domain/Response.ts new file mode 100644 index 0000000..377a55b --- /dev/null +++ b/bindings/js/src/core/domain/Response.ts @@ -0,0 +1,33 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {GenericOperationUnit} from "./GenericOperationUnit"; +import {Clojure} from "../../Clojure"; +import {DomainModel} from "./DomainModel"; + +/** + * Information about the response returned by an operation, associated to a particular status + */ +export class Response extends GenericOperationUnit { + + /** + * HTTP status code for the response + * @return + */ + public getStatusCode(): string | undefined { + return Clojure.amf_domain.status_code(this.rawModel); + } + + public setStatusCode(statusCode: string | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("status-code"), statusCode); + } + + public static build(id: string): Response { + return new Response(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedResponse, + id)); + } + +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/SourceMap.ts b/bindings/js/src/core/domain/SourceMap.ts new file mode 100644 index 0000000..913e8c4 --- /dev/null +++ b/bindings/js/src/core/domain/SourceMap.ts @@ -0,0 +1,35 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; +import {GenericTag} from "./GenericTag"; + +/** + * Lexical information generated by the parser. SourceMaps are optional and can + * be used to reconstruct the original lexical structure of the model during generation + */ +export class SourceMap extends DomainModel { + + /** + * Each unit of lexical information stored as a tag + * @returns {GenericTag[]} + */ + public getTags(): GenericTag[] { + let tags = Clojure.amf_domain.tags(this.rawModel); + return Clojure.cljsMap(tags, (e) => new GenericTag(e)); + } + + public setTags(tags: GenericTag[]) { + const newTags = tags.map(e => e.clojureModel()); + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("tags"), Clojure.toCljsSeq(newTags)); + } + + public static build(id: string): SourceMap { + return new SourceMap(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_document.map__GT_DocumentSourceMap, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/core/domain/Type.ts b/bindings/js/src/core/domain/Type.ts new file mode 100644 index 0000000..31e69bd --- /dev/null +++ b/bindings/js/src/core/domain/Type.ts @@ -0,0 +1,38 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DomainModel} from "./DomainModel"; +import {Clojure} from "../../Clojure"; + +export type JSONLD = Object + +/** + * Data Shape that describing a set of constraints over an operation unit payload + */ +export class Type extends DomainModel { + + /** + * JSON-LD data structure containing a SHACL shape that can be used to validate payloads for this operation unit + * @return + */ + public getShape(): JSONLD | undefined { + return Clojure.cljsToJs(Clojure.amf_domain.shape(this.rawModel)); + } + + /** + * Sets the SHACL shape for the payloads of this operation unit + * @param shaclShape valid SHACL shape encoded as JSON-LD string + */ + public setShape(shape: JSONLD | undefined) { + this.rawModel = Clojure.amf.update(DomainModel.domain_builder, this.rawModel, Clojure.kw("shape"), Clojure.jsToCljs(shape)); + } + + + public static build(id: string): Type { + return new Type(Clojure.amf.build( + DomainModel.domain_builder, + Clojure.amf_domain.map__GT_ParsedType, + id)); + } +} \ No newline at end of file diff --git a/bindings/js/src/examples/BasicFunctionality.ts b/bindings/js/src/examples/BasicFunctionality.ts new file mode 100644 index 0000000..92edbf0 --- /dev/null +++ b/bindings/js/src/examples/BasicFunctionality.ts @@ -0,0 +1,103 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {RAMLParser} from "../parsers/RAMLParser"; +import {APIDocumentation} from "../core/domain/APIDocumentation"; +import {Clojure} from "../Clojure"; +import {EndPoint} from "../core/domain/EndPoint"; +import {Module} from "../core/document/Module"; +import {Type} from "../core/domain/Type"; +import {AMF} from "../../index"; + +// Parsing +let apiFile = "http://test.com/something/api.raml"; +let cacheDirs = {'cacheDirs': {"http://test.com/something":"/Users/antoniogarrote/Development/api-modelling-framework/resources/other-examples/world-music-api"}} +AMF.RAMLParser.parseFile(apiFile, cacheDirs, (err, model) => { + try { + console.log("BACK FROM PARSING"); + console.log(err == null); + console.log(model != null); + console.log(model.constructor["name"]); + console.log("FINDING REFERENCES"); + + // references + let references = model.references().map(ref => { + console.log(ref); + return ref; + }); + if (references[0] != null) { + console.log(`FINDING MODEL FOR ${references[0]}`); + let foundModel = model.modelForReference(references[0]); + console.log(foundModel != null); + console.log(`FOUND MODEL WITH LOCATION ${foundModel.location()} FOR REFERENCE ${references[0]}`); + } + + // encodes + console.log("RETRIEVING ENCODED ELEMENT"); + let api = model.encodes() as APIDocumentation; + console.log(api != null); + + // displaying endpoints + let endpoints = api.getEndPoints(); + console.log("EndPoints"); + console.log("SOMETHING? " + Clojure.amf_document.id(api.clojureModel())); + console.log(api.getId()); + endpoints.forEach(e => { + console.log("ONE ENDPOINT"); + console.log(`${e.getId()} => ${e.getPath()}`) + }); + + // updating endponts + const before = endpoints.length; + console.log("BEFORE COUNT " + before); + let newEndpoint = EndPoint.build("http://test.com/external/1"); + newEndpoint.setPath("/lala"); + const afterBreakPoints = endpoints.concat([newEndpoint]); + console.log("SETTING THE ENDPOINTS"); + api.setEndPoints(afterBreakPoints); + + // displaying updated endpoints + endpoints = api.getEndPoints(); + endpoints.forEach(e => { + console.log("ONE ENDPOINT"); + console.log(`${Clojure.cljsToJs(e.getId())} => ${e.getPath()}`) + }); + const after = endpoints.length; + console.log("AFTER COUNT " + after); + + // resolution + const resolvedModel = model.resolve(); + endpoints = (resolvedModel.encodes() as APIDocumentation).getEndPoints(); + console.log("Resolved EndPoints"); + endpoints.forEach(e => { + console.log("ONE ENDPOINT"); + console.log(`${e.getId()} => ${e.getPath()}`) + }); + + // raw text + console.log(model.rawText() != null) + + // declarations + let module = model.references() + .map(ref => model.modelForReference(ref)) + .filter(m => m instanceof Module)[0] as Module; + console.log(module != null); + + let declared = module.declares(); + console.log(declared.length === 3); + + // types + let types = declared.filter(d => d instanceof Type) as Type[]; + console.log(types.length === 3); + types.forEach(t => console.log(t.getShape() != null)); + + // Generations + AMF.RAMLGenerator.generateString(model, null, null, (e, r) => console.log(r != null)); + AMF.OpenAPIGenerator.generateString(model, null, null, (e, r) => console.log(r != null)); + AMF.JSONLDGenerator.generateString(model, null, {'full-graph?': true, 'source-maps?': true}, (e, r) => r != null); + } catch (e) { + console.log("EXCEPTION!!"); + console.log(e); + } +}); \ No newline at end of file diff --git a/bindings/js/src/generators/AMFJSONLDGenerator.ts b/bindings/js/src/generators/AMFJSONLDGenerator.ts new file mode 100644 index 0000000..515b348 --- /dev/null +++ b/bindings/js/src/generators/AMFJSONLDGenerator.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseGenerator} from "./BaseGenerator"; +import {Clojure} from "../Clojure"; + +/** + * Generator that exports the AMF model as a JSON-LD document + */ +export class AMFJSONDGenerator extends BaseGenerator { + protected generator(): any { + return new Clojure.amf.__GT_APIModelGenerator(); + } +} \ No newline at end of file diff --git a/bindings/js/src/generators/BaseGenerator.ts b/bindings/js/src/generators/BaseGenerator.ts new file mode 100644 index 0000000..9a325c7 --- /dev/null +++ b/bindings/js/src/generators/BaseGenerator.ts @@ -0,0 +1,50 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DocumentModel} from "../core/document/DocumentModel"; +import {GenerationOptions} from "./GenerationOptions"; +import {Clojure} from "../Clojure"; + +/** + * Basic interface for all AMF generators. It allows to generate syntax files out of AMF Document Models. + */ +export abstract class BaseGenerator { + + /** + * Serialises the model and stores it in the provided file path and options + * @param path Path where the model will be serialised + * @param model DocumentModel to be serialised + * @param options Generation options + * @param cb Callback that will be invoked when the generation has finished reporting potential errors + */ + public generateFile(model: DocumentModel, path: string, options: GenerationOptions = {}, cb:(e?: Error) => void) { + Clojure.amf.generate_file(this.generator(), path, model.clojureModel(), options, (e) => { + if (e != null) { + cb(e); + } else { + cb(null); + } + }); + } + + /** + * Serialises the model and uses the provided file path as the default model location, applying the provided options + * @param path Path where the model will be serialised + * @param model DocumentModel to be serialised + * @param options Geneartion otions + * @param cb Callback that will be invoked when the generation has finished reporting potential errors and returning + * the generated text + */ + public generateString(model: DocumentModel, path: string | null = null, options: GenerationOptions = {}, cb:(e?: Error, text?: string) => void) { + Clojure.amf.generate_string(this.generator(), path || model.location(), model.clojureModel(), options, (e, result) => { + if (e != null) { + cb(e, null); + } else { + cb(null, result); + } + }); + } + + protected abstract generator(): any; +} \ No newline at end of file diff --git a/bindings/js/src/generators/GenerationOptions.ts b/bindings/js/src/generators/GenerationOptions.ts new file mode 100644 index 0000000..df6e1c5 --- /dev/null +++ b/bindings/js/src/generators/GenerationOptions.ts @@ -0,0 +1,22 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +/** + * Options for model serialisation + */ +export interface GenerationOptions { + /** + * When serialising to JSON-LD, enables or disables the generation of source-maps + * @param shouldGenerate + */ + 'source-maps?'?: boolean; + + /** + * When serialising into JSON-LD, if set to true, all the JSON-LD RDF graph for referenced documents will be nested inside + * the JSON-LD document of the model. + * If set to false, only the URI will be serialised + * @param shouldGenerateFullGraph + */ + 'full-graph?'?: boolean; +} \ No newline at end of file diff --git a/bindings/js/src/generators/OpenAPIGenerator.ts b/bindings/js/src/generators/OpenAPIGenerator.ts new file mode 100644 index 0000000..75a46b3 --- /dev/null +++ b/bindings/js/src/generators/OpenAPIGenerator.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseGenerator} from "./BaseGenerator"; +import {Clojure} from "../Clojure"; + +/** + * Serialises the AMF model as an OpenAPI JSON document + */ +export class OpenAPIGenerator extends BaseGenerator { + protected generator(): any { + return new Clojure.amf.__GT_OpenAPIGenerator(); + } +} \ No newline at end of file diff --git a/bindings/js/src/generators/RAMLGenerator.ts b/bindings/js/src/generators/RAMLGenerator.ts new file mode 100644 index 0000000..00a2c8d --- /dev/null +++ b/bindings/js/src/generators/RAMLGenerator.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseGenerator} from "./BaseGenerator"; +import {Clojure} from "../Clojure"; + +/** + * Serialised the AMF model as a RAML YAML document + */ +export class RAMLGenerator extends BaseGenerator { + protected generator(): any { + return new Clojure.amf.__GT_RAMLGenerator(); + } +} \ No newline at end of file diff --git a/bindings/js/src/parsers/AMFJSONLDParser.ts b/bindings/js/src/parsers/AMFJSONLDParser.ts new file mode 100644 index 0000000..d681929 --- /dev/null +++ b/bindings/js/src/parsers/AMFJSONLDParser.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseParser} from "./BaseParser"; +import {Clojure} from "../Clojure"; + +/** + * Wrapper class for the AMF OWL model parser, processes AMF model specification JSON-LD documents and generate the DocumentModel out of them + */ +export class AMFJSONLDParser extends BaseParser { + protected parser(): any { + return new Clojure.amf.__GT_APIModelParser(); + } +} \ No newline at end of file diff --git a/bindings/js/src/parsers/BaseParser.ts b/bindings/js/src/parsers/BaseParser.ts new file mode 100644 index 0000000..b31c4ca --- /dev/null +++ b/bindings/js/src/parsers/BaseParser.ts @@ -0,0 +1,52 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {DocumentModel} from "../core/document/DocumentModel"; +import {URL} from "../../index"; +import {Clojure} from "../Clojure"; + +/** + * Basic interface for all AMF parsers. It allows to parse syntax files and syntax text and generate the AMF Model out + * of it. + */ +export abstract class BaseParser { + + /** + * Generates a model parsing the file referenced by the provided URL and parsing options. + * @param url Local or remote URL + * @param options Parsing options + * @param cb Callback that will receive either the parsed model or error + * @return The parsed model + */ + public parseFile(url: URL, options: ParsingOptions = {}, cb: (err?: Error, model?: DocumentModel) => void) { + Clojure.amf.parse_file(this.parser(), url, Clojure.jsToCljs(options), (err, model) => { + if (err) { + cb(err) + } else { + cb(undefined, DocumentModel.fromRawModel(model)); + } + }); + } + + /** + * Generates a model parsing the provided textual input syntax and parsing options. + * @param text Input syntax to parse + * @param url Base URL for the document being parsed. It will be the base URL for the inclusions in this file + * @param options Parsing options + * @param cb callback that will receive either the parsed model or error + * @return The parsed Model + */ + public parseString(text: string, url: URL, options: ParsingOptions = {}, cb: (err?: Error, model?: DocumentModel) => void) { + Clojure.amf.parse_string(this.parser(), url, Clojure.jsToCljs(options), (err, model) => { + if (err) { + cb(err) + } else { + cb(undefined, DocumentModel.fromRawModel(model)); + } + }); + } + + + protected abstract parser(): any; +} \ No newline at end of file diff --git a/bindings/js/src/parsers/OpenAPIParser.ts b/bindings/js/src/parsers/OpenAPIParser.ts new file mode 100644 index 0000000..50a447a --- /dev/null +++ b/bindings/js/src/parsers/OpenAPIParser.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseParser} from "./BaseParser"; +import {Clojure} from "../Clojure"; + +/** + * Wrapper class for the AMF OpenAPI parser, processes OpenAPI specification documents and generate the DocumentModel out of them + */ +export class OpenAPIParser extends BaseParser { + protected parser(): any { + return new Clojure.amf.__GT_OpenAPIParser(); + } +} \ No newline at end of file diff --git a/bindings/js/src/parsers/ParsingOptions.ts b/bindings/js/src/parsers/ParsingOptions.ts new file mode 100644 index 0000000..33fd48b --- /dev/null +++ b/bindings/js/src/parsers/ParsingOptions.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +/** + * Parsing options for parsing + */ +interface ParsingOptions { + + /** + * Sets a mapping from URL prefixes for references to local directories to resolve references in the syntax + * @param uriToDirs + */ + cacheDirs?: {[url: string]: string}; +} \ No newline at end of file diff --git a/bindings/js/src/parsers/RAMLParser.ts b/bindings/js/src/parsers/RAMLParser.ts new file mode 100644 index 0000000..4c8d12c --- /dev/null +++ b/bindings/js/src/parsers/RAMLParser.ts @@ -0,0 +1,15 @@ +/** + * Created by antoniogarrote on 05/05/2017. + */ + +import {BaseParser} from "./BaseParser"; +import {Clojure} from "../Clojure"; + +/** + * Wrapper class for the AMF RAML parser, processes RAML specification documents and generate the DocumentModel out of them + */ +export class RAMLParser extends BaseParser { + protected parser(): any { + return new Clojure.amf.__GT_RAMLParser(); + } +} \ No newline at end of file diff --git a/bindings/js/tsconfig.json b/bindings/js/tsconfig.json new file mode 100644 index 0000000..ab8cd74 --- /dev/null +++ b/bindings/js/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "sourceMap": true, + "removeComments": false, + "typeRoots" : [ + ] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/bindings/js/typings.json b/bindings/js/typings.json new file mode 100644 index 0000000..9c0af25 --- /dev/null +++ b/bindings/js/typings.json @@ -0,0 +1,7 @@ +{ + "name": "amf-js", + "dependencies": {}, + "globalDependencies": { + "node": "registry:dt/node#7.0.0+20170322231424" + } +} diff --git a/build/api_modeling_framework/build.clj b/build/api_modeling_framework/build.clj index 5c07316..273b8b8 100644 --- a/build/api_modeling_framework/build.clj +++ b/build/api_modeling_framework/build.clj @@ -26,14 +26,23 @@ :repository repository :dependencies npm-dependencies}) + ;; Commands (defn sh! [& args] (println "==> " (string/join " " args)) (let [{:keys [err out exit]} (apply jsh/sh args)] + (println out) (if (not= exit 0) (throw (Exception. err)) (clojure.string/split-lines out)))) +(defn pipe! [args output] + (println "==> " (string/join " " args) " > " output) + (let [{:keys [err out exit]} (apply jsh/sh args)] + (if (not= exit 0) + (throw (Exception. err)) + (spit output out)))) + (defn mkdir [path] (sh! "mkdir" "-p" path)) @@ -55,6 +64,9 @@ (defn cp [from to] (sh! "cp" "-rf" from to)) +(defn mv [from to] + (sh! "mv" from to)) + (defn pwd [] (first (sh! "pwd"))) (defn ln [source target] @@ -70,8 +82,19 @@ (defn npm-install [path] (sh! "npm" "--prefix" path "install")) +(defn gulp [path task] + (sh! "./bindings/js/node_modules/.bin/gulp" "--gulpfile" path task)) + (defn local-gulp [from] (str from "/node_modules/.bin/gulp")) +(defn npm-publish-link [package path node_modules_target] + (let [target-package (str node_modules_target "/" package)] + (sh! "rm" "-f" target-package) + (sh! "ln" "-s" path target-package))) + +(defn npm-link-package [package] + (sh! "npm" "link" "package")) + (defn tsc [bin project] (sh! bin "-p" project)) @@ -123,6 +146,64 @@ ;; copied from package_files/index.js (rm "output/node/js/amf.js")) +(defn compile-js-bindings [] + (npm-install "bindings/js") + (gulp "./bindings/js/gulpfile.js" "typings") + (gulp "./bindings/js/gulpfile.js" "compile")) + +(defn build-js-bindings-web [] + (println "** Building Target: js-bindings-web\n") + (build "bindings" cljsbuild) + + (println "* copy package index file") + (let [deps ["global.cljs = cljs;" + "global.api_modeling_framework = api_modeling_framework;"] + data (slurp "output/bindings/amf.js") + data (reduce (fn [acc l] (str acc "\n" l)) data deps)] + (spit "output/bindings/index.js" data)) + + (println "generating npm package") + (-> (npm-package) + (json/generate-string {:pretty true}) + (->> (spit "output/bindings/package.json"))) + + (mkdir "bindings/js/node_modules") + (println "linking generating package") + (npm-publish-link "api-modeling-framework" "output/bindings" "bindings/js/node_modules") + + (println "Building bindings") + (sh! "npm" "link" "output/node") + (compile-js-bindings) + + (println "Running browserify") + (pipe! ["browserify" "bindings/js/index.js" "-s" "amf" "--ignore-missing"] "amf_bindings.js") + + + + (println "Cleaning bindings output") + (rm "output/bindings") + (rm "bindings/js/node_modules") + + (println "Copying output") + (sh! "mv" "amf_bindings.js" "output/amf_bindings.js")) + + +(defn build-js-bindings-node [] + (println "** Building Target: js-bindings-node\n") + + (build-node) + + (println "linking generating package") + (mkdir "bindings/js/node_modules") + (npm-publish-link "api-modeling-framework" "output/node" "bindings/js/node_modules") + + (compile-js-bindings) + + (println "Copying output") + (rm "output/amf-js") + (rm "bindings/js/node_modules") + (cp "bindings/js" "output/amf-js")) + (defn build-web [] (println "** Building Target: web\n") @@ -133,6 +214,8 @@ (condp = (first args) "web" (build-web) "node" (build-node) + "js-bindings-web" (build-js-bindings-web) + "js-bindings-node" (build-js-bindings-node) (do (println "Unknown task") (System/exit 2))) (catch Exception ex diff --git a/doc/java.md b/doc/java.md new file mode 100644 index 0000000..7a58acc --- /dev/null +++ b/doc/java.md @@ -0,0 +1,137 @@ +# AMF Java Programming Guide + +Java wrapping code for the AMF library can be found in the `org.raml.amf` namespace. +This package provides a Java friendly interface on top of the native Clojure code of the library. + +## Compiling + +At this moment we don't provide any artifacts in any repository to use AMF or these Java bindings, so they must be built manually with the help of Leiningen. +In order to build the library, you fist need to build an 'ubejar' with AMF. To accomplish this, from the main AMF project directory run the following instruction: + +``` bash +$ lein uberjar +``` +After Leiningen has finished you should have a standalone jar for AMF and all its dependencies in the `target/api-modeling-framework-0.1.2-SNAPSHOT-standalone.jar` location. Version might be different from the one at the moment of writing this documentation. + +Once the jar for AMF has been generated, we can generate the Java bindings jar. For that change to the `bindings/java` directory in the AMF project and use maven: + +``` bash +mvn package +``` + +After Maven has finished you should have an additional jar in the `target/amf-java-0.1.2-SNAPSHOT.jar` location with the Java bindings. Add both jars into your project to use the AMF Java bindings. + +## Parsing + +Parsers can be found in the `org.raml.amf.parsers` package of the project. They can be build using the factories in `org.raml.amf.AMF` + +```java +DocumentModel model = AMF.RAMLParser().parseFile(new URL("http://test.com/worldmusic/api.raml")); +``` + +Parsers are include for RAML, OpenAPI and the JSON-LD serialisation of the AMF model. + +Parsers can accept options, including a hash-map of URLs to local directories that will be used to resolve references in the parsed documents. + +For instance, in the next snippet all remote references to the URLs prefixed by `http://test.com/worldmusic` will be resolved looking into the local directory `/Users/antoniogarrote/world-music-api`. + +```java +HashMap cacheDirs = new HashMap<>(); +cacheDirs.put("http://test.com/worldmusic","/Users/antoniogarrote/vocabs/world-music-api"); +ParsingOptions options = new ParsingOptions().setCacheDirs(cacheDirs); + +DocumentDocument model = (Document) AMF.RAMLParser().parseFile(new URL("http://test.com/worldmusic/api.raml"), options); +``` +The original parsed text can be retrieved using the `rawText` method. + +## Navigating the Document Model +The parsing process will return an instance of one of the subclasses of `DocumentModel`. +Depending on what is the parsed file, a `Document`, a `Fragment` or a `Module` instance will be returned. + +No matter what is the actual Document Model class, the returned model will also include references to all linked documents in the model. + +These references can be listed using the `references` method, and new instances of `DocumentModel` can be built for these references using the `modelForReference` method: + +```java +for (URL ref : model.references()) { + DocumentModel refModel = model.modelForReference(ref); + System.out.println("Found a reference model: " + refModel); +} +``` + +## Applying resolution + +To run the resolution algorithm and combine all the documents from the Document Model into a single Domain Model description, the method `resolve` can be invoked. + +```java +DocumentModel resolvedModel = model.resolve(); +``` + +## Accessing the Domain Model + +The parsed Domain Model can be retrieved from the Document Model instance using the appropriate accessor. + +Fragments return the encoded Domain Model element using the `encodes` method from the `org.raml.amf.core.document.EncodesDomainModel` interface. +Modules returns the list of declared Domain Model elements using the `declares` method from the `org.raml.amf.core.document.DeclaresDomainModel` interface. +Documents can use both methods to retrieve the top level encoded element and the list of declared elements in the root element. + +```java +if (model instanceof EncodesDomainModel) { + System.out.println(model.encodes()); +} + +if (targetModel instanceof DeclaresDomainModel) { + List declarations = model.declares(); + for(DomainModel decl : declarations) { + System.out.println(decl); + } +} +``` + +## Navigating and mutating the Domain Model + +The Domain Model includes Java bean classes for all elements in the AMF Domain Model. +These getters and setters can be used to navigate and mutate the model. Please, refer to the [documentation](https://raml-org.github.io/api-modeling-framework/doc/java/apidocs/index.html) for more details. + +```java +APIDocumentation api = (APIDocumentation) model.encodes(); + +for (EndPoint endpoint : api.getEndpoints()) { + endpoint.setName("Modified " + endpoint.getName()); +} +``` + +## Serialisation + +AMF includes generators capable of serialising the AMF model back into one of the supported syntaxes. The method `generateString` can be used to generate a String representation, and the method `generateFile` can be used to dump the serialised model directly into a file. +Factory methods for each generator can be found in the `org.raml.amf.AMF` class. + + +```java +// Generating RAML +// Generate can accept just the model +String generated = AMF.RAMLGenerator().generateString(targetModel); +System.out.println(generated); + +// Generating OpenAPI +// It can also accept a destination File/URL for the model +generated = AMF.OpenAPIGenerator().generateString( + new File("world_music.json"), + targetModel +); +System.out.println(generated); + +// Generating JSON-LD +// Finally it can also accept a set of generation options +generated = AMF.JSONLDGenerator().generateString( + new File("world_music.jsonld"), + targetModel, + new GenerationOptions() + .setFullgraph(true) + .setSourceMapGeneration(true)); +System.out.println(generated); +``` + +Two options are available when generating JSON-LD documents. +- `setFullGraph` will nest the JSON-LD graphs for the referenced documents in the model to be serialised, otherwise only URIs will be generated. +- `setSourceMapGeneration` enables or disables the generation of source maps JSON-LD information in the output. diff --git a/doc/js.md b/doc/js.md new file mode 100644 index 0000000..fd0fd13 --- /dev/null +++ b/doc/js.md @@ -0,0 +1,148 @@ +# AMF JS Programming Guide + +JavaScript wrapping is available as a node NPM package. +This package provides a JS friendly interface on top of the native ClojureScript code of the library. + +## Compiling + +At this moment we don't provide any artifacts in any repository to use AMF or these JS bindings, so they must be built manually with the help of Leiningen. +In order to build the NPM package, you first need to generate the node version of the AMF library. This can be accomplished using the following command: + +``` bash +$ lein node +``` + +When the compilation has finished the node package will be available in `output/node`. Next step is linking the package directory so it will be avaliable for NPM. + +``` bash +$ cd output/node && npm link +``` + +Finally we need to compile and link the JS bindings package. From the AMF top level directory execute: + +``` bash +$ cd bindings/js +$ npm link api-modeling-framework +$ npm install +$ tsc +$ npm link +``` + +Now you can go to your NPM project directory and link the AMF JS bindings: + +``` bash +$ npm link amf-js +``` + +If you start a node REPL in your project now you should be able to require the library: + +``` javascript +require("amf-js") +``` + +## Parsing + +Parsers can be found in the `parsers` package of the project. They can be build using the factories in the `AMF` class + +``` typescript +let apiFile = "file:///path/to/other-examples/world-music-api/api.raml"; +AMF.RAMLParser.parseFile(apiFile, {}, (err, model) => { + if (err != null) { + console.log(`Created model from file ${model.location()}`); + } +}); +``` + +Parsers are include for RAML, OpenAPI and the JSON-LD serialisation of the AMF model. + +Parsers can accept options, including a hash-map of URLs to local directories that will be used to resolve references in the parsed documents. + +For instance, in the next snippet all remote references to the URLs prefixed by `http://test.com/worldmusic` will be resolved looking into the local directory. + +```typescript +let apiFile = "http://test.com/something/api.raml"; +let cacheDirs = {'cacheDirs': {"http://test.com/something":"file:///path/to/other-examples/world-music-api/api.raml"}}; +AMF.RAMLParser.parseFile(apiFile, cacheDirs, (err, model) => { + if (err != null) { + console.log(`Created model from file ${model.location()}`); + } +}); +``` +The original parsed text can be retrieved using the `rawText` method. + + +## Navigating the Document Model +The parsing process will return an instance of one of the subclasses of `DocumentModel`. +Depending on what is the parsed file, a `Document`, a `Fragment` or a `Module` instance will be returned. + +No matter what is the actual Document Model class, the returned model will also include references to all linked documents in the model. + +These references can be listed using the `references` method, and new instances of `DocumentModel` can be built for these references using the `modelForReference` method: + +```typescript +let referenceModels = model + .references() + .map(ref => model.modelForReference(ref)); +``` +## Applying resolution + +To run the resolution algorithm and combine all the documents from the Document Model into a single Domain Model description, the method `resolve` can be invoked. + +```typescript +const resolvedModel: DocumentModel = model.resolve(); +``` + +## Accessing the Domain Model + +The parsed Domain Model can be retrieved from the Document Model instance using the appropriate accessor. + +Fragments return the encoded Domain Model element using the `encodes` method from the `document.EncodesDomainModel` interface. +Modules returns the list of declared Domain Model elements using the `declares` method from the `document.DeclaresDomainModel` interface. +Documents can use both methods to retrieve the top level encoded element and the list of declared elements in the root element. + +```typesocript +if (model instanceof EncodesDomainModel) { + console.log(model.encodes()); +} + +if (targetModel instanceof DeclaresDomainModel) { + model.declares().foreach(decl => console.log(decl); +} +``` +## Navigating and mutating the Domain Model + +The Domain Model includes matching classes for all elements in the AMF Domain Model. +These getters and setters can be used to navigate and mutate the model. Please, refer to the [documentation](https://raml-org.github.io/api-modeling-framework/doc/js/apidocs/index.html) for more details. + +```typescript +let endpoints = api.getEndPoints(); + +// updating endponts +const before = endpoints.length; +let newEndpoint = EndPoint.build("http://test.com/external/1"); +newEndpoint.setPath("/lala"); + +const afterBreakPoints = endpoints.concat([newEndpoint]); +api.setEndPoints(afterBreakPoints); + +// updated endpoints +endpoints = api.getEndPoints(); +const after = endpoints.length; + +console.log("Should be true " + (after == before + 1)); +``` +## Serialisation + +AMF includes generators capable of serialising the AMF model back into one of the supported syntaxes. The method `generateString` can be used to generate a String representation, and the method `generateFile` can be used to dump the serialised model directly into a file. +Factory methods for each generator can be found in the `AMF` class. + + +```typescript +AMF.RAMLGenerator.generateString(model, null, null, (e, r) => console.log(r != null)); +AMF.OpenAPIGenerator.generateString(model, null, null, (e, r) => console.log(r != null)); +AMF.JSONLDGenerator.generateString(model, null, {'full-graph?': true, 'source-maps?': true}, (e, r) => r != null); +``` + +Two options are available when generating JSON-LD documents. +- `full-graph?` will nest the JSON-LD graphs for the referenced documents in the model to be serialised, otherwise only URIs will be generated. +- `source-maps?` enables or disables the generation of source maps JSON-LD information in the output. diff --git a/js/externs.js b/js/externs.js index 77e1355..b0d3f0d 100644 --- a/js/externs.js +++ b/js/externs.js @@ -11,3 +11,5 @@ JS_REST.location = function () { }; var JS_AST = function () { }; var AMF_LOADING_EVENT = function () { }; +var api_modeling_framework = {}; +api_modeling_framework.core = {}; diff --git a/project.clj b/project.clj index ca3df49..2a963b1 100644 --- a/project.clj +++ b/project.clj @@ -19,6 +19,8 @@ ;; dev only [difform "1.1.2"]] + :aot [api-modeling-framework.model.domain] + :plugins [[lein-cljsbuild "1.1.5"] [lein-npm "0.6.2"] [lein-doo "0.1.7"]] @@ -27,10 +29,15 @@ [json-to-ast "2.0.0-alpha1.2"]]} :profiles {:build {:source-paths ["build"] - :main api-modeling-framework.build}} + :main api-modeling-framework.build} + :precomp {:aot [api-modeling-framework.model.domain] } + :java-compile {:source-paths [] + :java-source-paths ["java/src"]}} :aliases {"node" ["with-profile" "build" "run" "node"] "web" ["with-profile" "build" "run" "web"] + "js-bindings-web" ["with-profile" "build" "run" "js-bindings-web"] + "js-bindings-node" ["with-profile" "build" "run" "js-bindings-node"] "test-js" ["doo" "node" "test" "once"]} :cljsbuild {:builds { @@ -58,6 +65,21 @@ :externs ["js/externs.js"] :pretty-print true}} + :bindings {:source-paths ["src"] + :figwheel true + :compiler {:main api-modeling-framework.core + + :output-dir "output/bindings/" + :output-to "output/bindings/amf.js" + :optimizations :simple, + :target :nodejs + + :asset-path "/js" + :foreign-libs [{:file "js/js-support-bundle.js" + :provides ["api_modeling_framework.js-support"]}] + :externs ["js/externs.js"] + :pretty-print true}} + :test {:source-paths ["src" "src_node" "test"] :compiler {:output-dir "output/test/" :output-to "output/test/amf-test.js" diff --git a/src/api_modeling_framework/core.cljc b/src/api_modeling_framework/core.cljc index b772f65..4f785b6 100644 --- a/src/api_modeling_framework/core.cljc +++ b/src/api_modeling_framework/core.cljc @@ -1,6 +1,6 @@ (ns api-modeling-framework.core #?(:cljs (:require-macros [cljs.core.async.macros :refer [go]])) - #?(:clj (:require [clojure.core.async :refer [! go chan] :as async] + #?(:clj (:require [clojure.core.async :refer [! sync [f] + #?(:cljs (throw (js/Error "Synchronous version not supported")) + :clj (! c e) + (>! c res))))) + c)))) (defrecord ^:export RAMLParser [] Parser + (parse-file-sync [this uri] + (cb->sync (partial parse-file this uri))) + (parse-file-sync [this uri options] + (cb->sync (partial parse-file this uri options))) (parse-file [this uri cb] (parse-file this uri {} cb)) (parse-file [this uri options cb] (go (let [res (sync (partial parse-string this uri string))) + (parse-string-sync [this uri string options] + (cb->sync (partial parse-string this uri string options))) (parse-string [this uri string cb] (parse-string this uri string {} cb)) (parse-string [this uri string options cb] (go (let [res (sync (partial parse-file this uri))) + (parse-file-sync [this uri options] + (cb->sync (partial parse-file this uri options))) (parse-file [this uri cb] (parse-file this uri {} cb)) (parse-file [this uri options cb] (go (let [res (sync (partial parse-string this uri string))) + (parse-string-sync [this uri string options] + (cb->sync (partial parse-string this uri string options))) (parse-string [this uri string cb] (parse-string this uri string {} cb)) (parse-string [this uri string options cb] (go (let [res (sync (partial parse-file this uri))) + (parse-file-sync [this uri options] + (cb->sync (partial parse-file this uri options))) (parse-file [this uri cb] (parse-file this uri {} cb)) (parse-file [this uri options cb] (debug "Parsing APIModel file") @@ -138,6 +189,10 @@ (try (cb nil (to-model (jsonld-document-parser/from-jsonld res))) (catch #?(:clj Exception :cljs js/Error) ex (cb (platform/<-clj ex) nil))))))) + (parse-string-sync [this uri string] + (cb->sync (partial parse-string this uri string))) + (parse-string-sync [this uri string options] + (cb->sync (partial parse-string this uri string options))) (parse-string [this uri string cb] (parse-string this uri string {} cb)) (parse-string [this uri string options cb] (debug "Parsing APIModel string") @@ -150,6 +205,8 @@ (defrecord ^:export APIModelGenerator [] Generator + (generate-string-sync [this uri model options] (cb->sync (partial generate-string this uri model options))) + (generate-file-sync [this uri model options] (cb->sync (partial generate-file this uri model options))) (generate-string [this uri model options cb] (debug "Generating APIModel string") (go (try (let [options (keywordize-keys options) @@ -178,6 +235,8 @@ (defrecord ^:export RAMLGenerator [] Generator + (generate-string-sync [this uri model options] (cb->sync (partial generate-string this uri model options))) + (generate-file-sync [this uri model options] (cb->sync (partial generate-file this uri model options))) (generate-string [this uri model options cb] (debug "Generating RAML string") (go (try (let [options (keywordize-keys (merge (or (platform/->clj options) {}) @@ -207,6 +266,8 @@ (defrecord ^:export OpenAPIGenerator [] Generator + (generate-string-sync [this uri model options] (cb->sync (partial generate-string this uri model options))) + (generate-file-sync [this uri model options] (cb->sync (partial generate-file this uri model options))) (generate-string [this uri model options cb] (debug "Generating OpenAPI string") (go (try (let [options (keywordize-keys (merge (or (platform/->clj options) {}) @@ -278,6 +339,13 @@ (let [domain-cache (atom nil) lexical-cache-raml (atom {})] (reify Model + (unit-kind [_] + (cond + (and (satisfies? document/Module res) + (satisfies? document/Fragment res)) "document" + (satisfies? document/Module res) "module" + (satisfies? document/Fragment res) "fragment" + :else "unit")) (location [_] (document/location res)) (document-model [_] res) diff --git a/src/api_modeling_framework/model/domain.cljc b/src/api_modeling_framework/model/domain.cljc index f2c8987..0b672d6 100644 --- a/src/api_modeling_framework/model/domain.cljc +++ b/src/api_modeling_framework/model/domain.cljc @@ -32,8 +32,8 @@ CommonAPIProperties (host [this] host) (scheme [this] scheme) - (accepts [this] accepts) - (content-type [this] content-type) + (accepts [this] (or accepts [])) + (content-type [this] (or content-type [])) HeadersHolder (headers [this] (or headers [])) ParametersHolder