From 6977ab3fa7a1e4cd5ea9cc157062c65045f36559 Mon Sep 17 00:00:00 2001 From: Benjamin Geer Date: Fri, 28 Feb 2020 17:07:33 +0100 Subject: [PATCH] feat(api-v2): Remove client code generation (#1610) --- .../05-internals/design/client-api/index.md | 192 -- docs/src/paradox/05-internals/design/index.md | 1 - ...apis.md => generating-client-test-data.md} | 34 +- .../paradox/05-internals/development/index.md | 2 +- webapi/_test_data/all_data/admin-data.ttl | 4 +- .../org/knora/webapi/OntologyConstants.scala | 131 +- .../org/knora/webapi/SharedTestDataADM.scala | 14 +- ...minToApiV2ComplexTransformationRules.scala | 1899 ----------- .../ontologymessages/OntologyMessagesV2.scala | 4 +- .../OntologyTransformationRules.scala | 15 +- .../responders/v2/OntologyResponderV2.scala | 12 +- .../knora/webapi/routing/ClientApiRoute.scala | 59 +- .../webapi/routing/admin/AdminClientApi.scala | 147 +- .../webapi/routing/admin/GroupsRouteADM.scala | 143 +- .../webapi/routing/admin/ListsRouteADM.scala | 161 +- .../routing/admin/PermissionsRouteADM.scala | 66 +- .../routing/admin/ProjectsRouteADM.scala | 283 +- .../webapi/routing/admin/StoreRouteADM.scala | 2 +- .../webapi/routing/admin/UsersRouteADM.scala | 305 +- .../webapi/routing/v2/ListsRouteV2.scala | 24 +- .../webapi/routing/v2/OntologiesRouteV2.scala | 29 +- .../webapi/routing/v2/ResourcesRouteV2.scala | 77 +- .../webapi/routing/v2/SearchRouteV2.scala | 21 +- .../knora/webapi/routing/v2/V2ClientApi.scala | 79 +- .../webapi/routing/v2/ValuesRouteV2.scala | 217 +- .../org/knora/webapi/util/ClientApi.scala | 148 + .../knora/webapi/util/StringFormatter.scala | 60 +- .../webapi/util/clientapi/ClientApi.scala | 971 ------ .../ClientCollectionTypeParser.scala | 492 --- .../util/clientapi/GeneratorBackEnd.scala | 157 - .../util/clientapi/GeneratorFrontEnd.scala | 637 ---- .../util/clientapi/TypeScriptBackEnd.scala | 684 ---- .../generateKnoraApiConnection.scala.txt | 53 - .../generateTypeScriptClass.scala.txt | 57 - .../generateTypeScriptEndpoint.scala.txt | 62 - .../generateTypeScriptMainEndpoint.scala.txt | 59 - .../knoraApiOntologyWithValueObjects.jsonld | 7 +- .../knoraApiOntologyWithValueObjects.rdf | 2913 ++++++++--------- .../knoraApiOntologyWithValueObjects.ttl | 7 +- .../webapi/e2e/ClientApiRouteE2ESpec.scala | 57 +- .../knora/webapi/e2e/InstanceChecker.scala | 363 +- .../webapi/e2e/InstanceCheckerSpec.scala | 45 - .../webapi/e2e/admin/ProjectsADME2ESpec.scala | 6 +- .../admin/ProjectsResponderADMSpec.scala | 4 +- .../webapi/util/StringFormatterSpec.scala | 25 - .../ClientCollectionTypeParserSpec.scala | 175 - .../clientapi/SourceCodeFilePathSpec.scala | 107 - .../webapi/util/jsonld/JsonLDUtilSpec.scala | 7 +- 48 files changed, 2119 insertions(+), 8898 deletions(-) delete mode 100644 docs/src/paradox/05-internals/design/client-api/index.md rename docs/src/paradox/05-internals/development/{generating-client-apis.md => generating-client-test-data.md} (56%) delete mode 100644 webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala create mode 100644 webapi/src/main/scala/org/knora/webapi/util/ClientApi.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParser.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorBackEnd.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/util/clientapi/TypeScriptBackEnd.scala delete mode 100644 webapi/src/main/twirl/clientapi/typescript/generateKnoraApiConnection.scala.txt delete mode 100644 webapi/src/main/twirl/clientapi/typescript/generateTypeScriptClass.scala.txt delete mode 100644 webapi/src/main/twirl/clientapi/typescript/generateTypeScriptEndpoint.scala.txt delete mode 100644 webapi/src/main/twirl/clientapi/typescript/generateTypeScriptMainEndpoint.scala.txt delete mode 100644 webapi/src/test/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParserSpec.scala delete mode 100644 webapi/src/test/scala/org/knora/webapi/util/clientapi/SourceCodeFilePathSpec.scala diff --git a/docs/src/paradox/05-internals/design/client-api/index.md b/docs/src/paradox/05-internals/design/client-api/index.md deleted file mode 100644 index 76cb9f09c9..0000000000 --- a/docs/src/paradox/05-internals/design/client-api/index.md +++ /dev/null @@ -1,192 +0,0 @@ - - -# Client API Code Generation Framework - -@@toc - -## Requirements - -* Simplify the development of clients that work with Knora APIs. -* Reduce the need for manual changes in client code when Knora APIs change. -* At minimum, generate client API code in - * TypeScript - * Python -* Generate: - * Endpoint definitions containing function definitions in the target language. - * Class definitions corresponding to the built-in classes that Knora uses in its APIs. -* Include client function definitions in Knora route definitions. - -In the future, it would also be useful to generate project-specific client -APIs, with class definitions corresponding to project-specific classes. - -## Implementation - -Client APIs are defined in Scala and extend the `ClientApi` trait. There -is currently an implementation for the admin API, called `AdminClientApi`. -A `ClientApi` contains one or more `KnoraRoute` implementations that extend -`ClientEndpoint`. Each endpoint defines functions to be generated for performing -API operations that use the route. - -The route `ClientApiRoute` generates all available client APIs for a specified -target, returning source code in a Zip file. For instructions on using -this route, see -@ref:[Generating Client API Code](../../development/generating-client-apis.md). - -This route has a front end, `GeneratorFrontEnd`, which that gets API class -definitions from `OntologyResponderV2` and transforms them into a data structure -that is suitable for code generation. The route supports different back ends for -different targets. A back end determines which files need to be generated, -generates each file using a Twirl template, and arranges the files in the -correct directory structure. - -Currently one back end, `TypeScriptBackEnd`, is implemented; it generates code -for use with [knora-api-js-lib](https://github.com/dasch-swiss/knora-api-js-lib). - -## Client Function DSL - -Client function definitions are written in a Scala DSL. A function definition -looks like this: - -@@snip [UsersRouteADM.scala]($src$/org/knora/webapi/routing/admin/UsersRouteADM.scala) { #getUserGroupMembershipsFunction } - -The `description` keyword specifies a documentation comment describing the function. -A function has `params`, each of which also has a `description`, as well as a `paramType`. -Built-in types are defined in `ClientApi.scala` and extend `ClientObjectType`. -Class types can be constructed using the `classRef` function, as shown above. - -The `doThis` keyword introduces the body of a function, which can be either -an HTTP operation or a function call. After the `doThis` block, `returns` -specifies the return type of the function. - -### HTTP Operations - -An HTTP operation is introduced by `httpGet`, `httpPost`, `httpPut`, or -`httpDelete`; it takes a `path` and (if it `httpPost` or `httpPut`) an optional -request body. The path consists of elements separated by slashes. Each element -is either `str()` representing a string literal, `arg` representing an argument -that was passed to the function, or `argMember()` representing a member of an -argument. - -URL parameters can be added like this: - -@@snip [PermissionsRouteADM.scala]($src$/org/knora/webapi/routing/admin/ListsRouteADM.scala) { #getListsInProjectFunction } - -Here is an example with a request body: - -@@snip [UsersRouteADM.scala]($src$/org/knora/webapi/routing/admin/UsersRouteADM.scala) { #createUserFunction } - -In this case, the request body is the `user` argument that was passed to the function. - -The request body can also be a constructed JSON object: - -@@snip [UsersRouteADM.scala]($src$/org/knora/webapi/routing/admin/UsersRouteADM.scala) { #updateUserPasswordFunction } - -### Function Calls - -Instead of performing an HTTP operation directly, a function can call another -function, like this: - -@@snip [UsersRouteADM.scala]($src$/org/knora/webapi/routing/admin/UsersRouteADM.scala) { #getUserFunction } -@@snip [UsersRouteADM.scala]($src$/org/knora/webapi/routing/admin/UsersRouteADM.scala) { #getUserByIriFunction } - -If an argument of the calling function needs to be converted to another type -for the function call, use the `as` keyword as shown above. - -## Generated Classes - -Many objects have a unique ID, which is present when the object is read or -updated, but not when it is created. - -API classes can also have read-only properties. For example, in the admin API, -the `User` class has a `projects` property, whose objects are instances of -`Project`. Similarly, the `Projects` class has a `members` property, whose -objects are instances of `User`. However, when users and projects are created or -updated, these properties are not used. - -In TypeScript, it is necessary to avoid circular imports. If the TypeScript -definition of `User` imports the definition of `Project`, the definition of -`Project` cannot import the definition of `User`. - -The structure of the generated classes is intended to deal with these issues. -Taking `User` and `Project` as an example: - -- The `User` class does not contain an ID or any read-only properties. - -- A `StoredUser` class is generated as a subclass of `User`. It provides - the user's ID, and can be submitted in update operations. - -- A `ReadUser` class is generated as a subclass of `StoredUser`. It provides - the read-only properties. - -In `ReadUser`, the `projects` property is a collection of `StoredProject` -objects. Since `StoredProject` does not have any read-only properties, it -does not have a property referring to users. This prevents circular imports. - -This design works because in the Knora API, a circular reference always involves -a read-only property. For example, the `projects` property of `User` is -read-only, as is the `members` property of `Project`. In the case of a resource, -the property pointing from a resource to a link value is not read-only (you can -submit a resource with an embedded link value), but the property pointing from a -link value to an embedded resource is read-only. - -The read-only properties and ID properties are specified in each `ClientApi`. - -## Collection Types - -`Array[T]` and `Map[K, V]` collection types can be generated and used as the object types -of properties in ordinary classes. The collection type is specified in the IRI of the -property object type, using a Scala-like type annotation syntax, like this: - -``` -http://api.knora.org/ontology/knora-admin/v2#collection: Map[URI, Array[Permission]] -``` - -(The local part of the IRI can also be URL-encoded.) The keyword `collection:` indicates -that the rest of the IRI specifies a collection type, which must be an `Array` or `Map` type. -The following literal types can be used: - -- `String` -- `Boolean` -- `Integer` -- `Decimal` -- `URI` -- `DateTimeStamp` - -Class names (like `Permission`) in the example above refer to classes in the same IRI -namespace as the collection type. The keys of a `Map` must be `String` or `URI`. - -`ClientCollectionTypeParser` parses these definitions into `MapType` and `ArrayType` -objects, which can then be used by a language-specific back end to generate type signatures -in the target language. - -## Testing - -### Library Test Stubs - -The generated code depends on handwritten library code to work, but stubs can -be provided to test for compile errors in the generated code. - -The directory `webapi/_test_data/typescript-client-mock-src` in the Knora source -tree contains test stubs for the TypeScript client library. - -### Test Requests and Responses - -The generated code includes a directory `test-data`, containing sample requests -and responses, which can be used to test the generated code without Knora. diff --git a/docs/src/paradox/05-internals/design/index.md b/docs/src/paradox/05-internals/design/index.md index 5c9811604f..fe3cb49997 100644 --- a/docs/src/paradox/05-internals/design/index.md +++ b/docs/src/paradox/05-internals/design/index.md @@ -27,6 +27,5 @@ License along with Knora. If not, see . - [API v1 Design](api-v1/index.md) - [API v2 Design](api-v2/index.md) - [Admin API Design](api-admin/index.md) -- [Client API Code Generation Framework](client-api/index.md) @@@ diff --git a/docs/src/paradox/05-internals/development/generating-client-apis.md b/docs/src/paradox/05-internals/development/generating-client-test-data.md similarity index 56% rename from docs/src/paradox/05-internals/development/generating-client-apis.md rename to docs/src/paradox/05-internals/development/generating-client-test-data.md index e1db99503b..7dbac6aaad 100644 --- a/docs/src/paradox/05-internals/development/generating-client-apis.md +++ b/docs/src/paradox/05-internals/development/generating-client-test-data.md @@ -17,24 +17,30 @@ You should have received a copy of the GNU Affero General Public License along with Knora. If not, see . --> -# Generating Client API Code +# Generating Client Test Data -The following route returns a Zip file containing generated client API -code for the specified target: +@@toc -``` -HTTP GET to http://host/clientapi/TARGET -``` +## Requirements + +Generate test requests and responses for Knora's routes, to be used in testing +client code without the need for a running Knora instance. + +## Implementation -Currently the only supported `TARGET` is `typescript`. For documentation -on defining client APIs, see -@ref:[Client API Code Generation Framework](../design/client-api/index.md). +A class for each Knora API extends the `ClientApi` trait. +A `ClientApi` contains one or more `KnoraRoute` implementations that extend +`ClientEndpoint`. Each endpoint provides functions that return generated +client test data. -To check whether the generated TypeScript code compiles, without actually -integrating it into `knora-api-js-lib`, use: +The route `ClientApiRoute` returns a Zip file containing generated test data. +returning source code in a Zip file. + +## Usage + +The following route returns a Zip file containing generated client test +data: ``` -HTTP GET to http://host/clientapi/typescript?mock=true +HTTP GET to http://host/clientapitest ``` - -This adds mock TypeScript library dependencies. diff --git a/docs/src/paradox/05-internals/development/index.md b/docs/src/paradox/05-internals/development/index.md index 0b493edd4c..e3a6add1f5 100644 --- a/docs/src/paradox/05-internals/development/index.md +++ b/docs/src/paradox/05-internals/development/index.md @@ -35,6 +35,6 @@ License along with Knora. If not, see . - [Profiling Knora](profiling.md) - [Starting the Knora Stack inside Docker Container](docker-compose.md) - [Updating Repositories](updating-repositories.md) -- [Generating Client API Code](generating-client-apis.md) +- [Generating Client Test Data](generating-client-test-data.md) @@@ diff --git a/webapi/_test_data/all_data/admin-data.ttl b/webapi/_test_data/all_data/admin-data.ttl index 65f1335c20..9341e57857 100644 --- a/webapi/_test_data/all_data/admin-data.ttl +++ b/webapi/_test_data/all_data/admin-data.ttl @@ -392,7 +392,9 @@ Die Internetpublikation macht das digitalisierte Korpus dieser Frühdrucke durc knora-admin:projectLongname "Anything Project"^^xsd:string ; knora-admin:projectDescription "Anything Project"^^xsd:string ; knora-admin:status "true"^^xsd:boolean ; - knora-admin:hasSelfJoinEnabled "false"^^xsd:boolean . + knora-admin:hasSelfJoinEnabled "false"^^xsd:boolean ; + knora-admin:projectKeyword "things"^^xsd:string, + "arbitrary test data"^^xsd:string . a knora-admin:User ; knora-admin:username "anything.user01"^^xsd:string ; diff --git a/webapi/src/main/scala/org/knora/webapi/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/OntologyConstants.scala index 4092cbc966..53e63a600a 100644 --- a/webapi/src/main/scala/org/knora/webapi/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/OntologyConstants.scala @@ -539,109 +539,6 @@ object OntologyConstants { val AnonymousUser: IRI = KnoraAdminPrefixExpansion + "AnonymousUser" } - object KnoraAdminV2 { - val VersionSegment = "/v2" - val KnoraAdminOntologyIri: IRI = KnoraApi.ApiOntologyStart + KnoraAdmin.KnoraAdminOntologyLabel + VersionSegment - val KnoraAdminPrefixExpansion: IRI = KnoraAdminOntologyIri + "#" - val UserResponse: IRI = KnoraAdminPrefixExpansion + "UserResponse" - val UsersResponse: IRI = KnoraAdminPrefixExpansion + "UsersResponse" - val ProjectResponse: IRI = KnoraAdminPrefixExpansion + "ProjectResponse" - val ProjectsResponse: IRI = KnoraAdminPrefixExpansion + "ProjectsResponse" - val GroupResponse: IRI = KnoraAdminPrefixExpansion + "GroupResponse" - val GroupsResponse: IRI = KnoraAdminPrefixExpansion + "GroupsResponse" - val Lists: IRI = KnoraAdminPrefixExpansion + "lists" - val ListsResponse: IRI = KnoraAdminPrefixExpansion + "ListsResponse" - val NodeInfo: IRI = KnoraAdminPrefixExpansion + "nodeinfo" - val ListNodeInfo: IRI = KnoraAdminPrefixExpansion + "ListNodeInfo" - val ListNode: IRI = KnoraAdminPrefixExpansion + "ListNode" - val CreateListRequest: IRI = KnoraAdminPrefixExpansion + "CreateListRequest" - val CreateChildNodeRequest: IRI = KnoraAdminPrefixExpansion + "CreateChildNodeRequest" - val ListResponse: IRI = KnoraAdminPrefixExpansion + "ListResponse" - val UpdateListInfoRequest: IRI = KnoraAdminPrefixExpansion + "UpdateListInfoRequest" - val ListNodeInfoResponse: IRI = KnoraAdminPrefixExpansion + "ListNodeInfoResponse" - val ListInfoResponse: IRI = KnoraAdminPrefixExpansion + "ListInfoResponse" - val ListClass: IRI = KnoraAdminPrefixExpansion + "List" - val ListIri: IRI = KnoraAdminPrefixExpansion + "listIri" - val ParentNodeIri: IRI = KnoraAdminPrefixExpansion + "parentNodeIri" - val Labels: IRI = KnoraAdminPrefixExpansion + "labels" - val Comments: IRI = KnoraAdminPrefixExpansion + "comments" - val Position: IRI = KnoraAdminPrefixExpansion + "position" - val IsRootNode: IRI = KnoraAdminPrefixExpansion + "isRootNode" - val HasRootNode: IRI = KnoraAdminPrefixExpansion + "hasRootNode" - val Children: IRI = KnoraAdminPrefixExpansion + "children" - val Members: IRI = KnoraAdminPrefixExpansion + "members" - val MembersResponse: IRI = KnoraAdminPrefixExpansion + "MembersResponse" - val AdministrativePermissionResponse: IRI = KnoraAdminPrefixExpansion + "AdministrativePermissionResponse" - val AdministrativePermissionsResponse: IRI = KnoraAdminPrefixExpansion + "AdministrativePermissionsResponse" - val Users: IRI = KnoraAdminPrefixExpansion + "users" - val UserClass: IRI = KnoraAdminPrefixExpansion + "User" - val UpdateUserRequest: IRI = KnoraAdminPrefixExpansion + "UpdateUserRequest" - val UserProperty: IRI = KnoraAdminPrefixExpansion + "user" - val Username: IRI = KnoraAdminPrefixExpansion + "username" - val Email: IRI = KnoraAdminPrefixExpansion + "email" - val GivenName: IRI = KnoraAdminPrefixExpansion + "givenName" - val FamilyName: IRI = KnoraAdminPrefixExpansion + "familyName" - val Lang: IRI = KnoraAdminPrefixExpansion + "lang" - val Projects: IRI = KnoraAdminPrefixExpansion + "projects" - val ProjectsAdmin: IRI = KnoraAdminPrefixExpansion + "projectsAdmin" - val Groups: IRI = KnoraAdminPrefixExpansion + "groups" - val SystemAdmin: IRI = KnoraAdminPrefixExpansion + "systemAdmin" - val ProjectClass: IRI = KnoraAdminPrefixExpansion + "Project" - val ID: IRI = KnoraAdminPrefixExpansion + "id" - val Token: IRI = KnoraAdminPrefixExpansion + "token" - val Password: IRI = KnoraAdminPrefixExpansion + "password" - val SessionID: IRI = KnoraAdminPrefixExpansion + "sessionId" - val Shortname: IRI = KnoraAdminPrefixExpansion + "shortname" - val Longname: IRI = KnoraAdminPrefixExpansion + "longname" - val Shortcode: IRI = KnoraAdminPrefixExpansion + "shortcode" - val ProjectDescription: IRI = KnoraAdminPrefixExpansion + "projectDescription" - val GroupDescription: IRI = KnoraAdminPrefixExpansion + "groupDescription" - val Status: IRI = KnoraAdminPrefixExpansion + "status" - val Keywords: IRI = KnoraAdminPrefixExpansion + "keywords" - val Logo: IRI = KnoraAdminPrefixExpansion + "logo" - val Ontologies: IRI = KnoraAdminPrefixExpansion + "ontologies" - val SelfJoin: IRI = KnoraAdminPrefixExpansion + "selfjoin" - val GroupClass: IRI = KnoraAdminPrefixExpansion + "Group" - val GroupProperty: IRI = KnoraAdminPrefixExpansion + "group" - val ProjectProperty: IRI = KnoraAdminPrefixExpansion + "project" - val ProjectIri: IRI = KnoraAdminPrefixExpansion + "projectIri" - val ProjectWithIriObj: IRI = KnoraAdminPrefixExpansion + "projectWithIriObj" - val AdministrativePermissionProperty: IRI = KnoraAdminPrefixExpansion + "administrative_permission" - val AdministrativePermissions: IRI = KnoraAdminPrefixExpansion + "administrative_permissions" - val AdministrativePermissionClass: IRI = KnoraAdminPrefixExpansion + "AdministrativePermission" - val ProjectRestrictedViewSettings: IRI = KnoraAdminPrefixExpansion + "ProjectRestrictedViewSettings" - val ProjectRestrictedViewSettingsResponse: IRI = KnoraAdminPrefixExpansion + "ProjectRestrictedViewSettingsResponse" - val Settings: IRI = KnoraAdminPrefixExpansion + "settings" - val Size: IRI = KnoraAdminPrefixExpansion + "size" - val Watermark: IRI = KnoraAdminPrefixExpansion + "watermark" - val ForGroup: IRI = KnoraAdminPrefixExpansion + "forGroup" - val ForProject: IRI = KnoraAdminPrefixExpansion + "forProject" - val ForResourceClass: IRI = KnoraAdminPrefixExpansion + "forResourceClass" - val ForProperty: IRI = KnoraAdminPrefixExpansion + "forProperty" - val Permission: IRI = KnoraAdminPrefixExpansion + "Permission" - val HasPermissions: IRI = KnoraAdminPrefixExpansion + "hasPermissions" - val AdditionalInformation: IRI = KnoraAdminPrefixExpansion + "additionalInformation" - val PermissionCode: IRI = KnoraAdminPrefixExpansion + "permissionCode" - val Iri: IRI = KnoraAdminPrefixExpansion + "iri" - val Name: IRI = KnoraAdminPrefixExpansion + "name" - val ListProperty: IRI = KnoraAdminPrefixExpansion + "list" - val ListInfoProperty: IRI = KnoraAdminPrefixExpansion + "listinfo" - val KeywordsProperty: IRI = KnoraAdminPrefixExpansion + "keywords" - val KeywordsResponse: IRI = KnoraAdminPrefixExpansion + "KeywordsResponse" - val CreateGroupRequest: IRI = KnoraAdminPrefixExpansion + "CreateGroupRequest" - val UpdateGroupRequest: IRI = KnoraAdminPrefixExpansion + "UpdateGroupRequest" - val UpdateProjectRequest: IRI = KnoraAdminPrefixExpansion + "UpdateProjectRequest" - val StringLiteral: IRI = KnoraAdminPrefixExpansion + "StringLiteral" - val Value: IRI = KnoraAdminPrefixExpansion + "value" - val Language: IRI = KnoraAdminPrefixExpansion + "language" - val Permissions: IRI = KnoraAdminPrefixExpansion + "permissions" - val PermissionsData: IRI = KnoraAdminPrefixExpansion + "PermissionsData" - val GroupsPerProject: IRI = KnoraAdminPrefixExpansion + "groupsPerProject" - val AdministrativePermissionsPerProject: IRI = KnoraAdminPrefixExpansion + "administrativePermissionsPerProject" - val AdministrativePermissionsPerProjectCollectionType: IRI = KnoraAdminPrefixExpansion + "collection: Map[URI, Array[Permission]]" - val GroupsPerProjectCollectionType: IRI = KnoraAdminPrefixExpansion + "collection: Map[URI, Array[URI]]" - } - object Standoff { val StandoffOntologyLabel: String = "standoff" val StandoffOntologyIri: IRI = KnoraInternal.InternalOntologyStart + "/" + StandoffOntologyLabel @@ -1180,18 +1077,8 @@ object OntologyConstants { KnoraBase.ValueHasColor -> KnoraApiV2Complex.ColorValueAsColor, KnoraBase.ValueHasStandoff -> KnoraApiV2Complex.TextValueHasStandoff, KnoraBase.PageCount -> KnoraApiV2Complex.DocumentFileValueHasPageCount, - KnoraAdmin.PreferredLanguage -> KnoraAdminV2.Lang, - KnoraAdmin.IsInProject -> KnoraAdminV2.Projects, - KnoraAdmin.IsInSystemAdminGroup -> KnoraAdminV2.SystemAdmin, - KnoraAdmin.KnoraProject -> KnoraAdminV2.ProjectClass, - KnoraAdmin.ProjectShortname -> KnoraAdminV2.Shortname, - KnoraAdmin.ProjectLongname -> KnoraAdminV2.Longname, - KnoraAdmin.ProjectShortcode -> KnoraAdminV2.Shortcode, - KnoraAdmin.ProjectKeyword -> KnoraAdminV2.Keywords, - KnoraAdmin.ProjectLogo -> KnoraAdminV2.Logo, - KnoraAdmin.UserGroup -> KnoraAdminV2.GroupClass, - KnoraAdmin.BelongsToProject -> KnoraAdminV2.ProjectProperty, - KnoraAdmin.HasSelfJoinEnabled -> KnoraAdminV2.SelfJoin + KnoraAdmin.KnoraProject -> Xsd.Uri, + KnoraAdmin.User -> Xsd.Uri ), (ApiV2Simple, InternalSchema) -> Map( // Not all types in ApiV2Simple can be converted here to types in KnoraBase. For example, @@ -1231,19 +1118,7 @@ object OntologyConstants { KnoraApiV2Complex.GeonameValueAsGeonameCode -> KnoraBase.ValueHasGeonameCode, KnoraApiV2Complex.ColorValueAsColor -> KnoraBase.ValueHasColor, KnoraApiV2Complex.TextValueHasStandoff -> KnoraBase.ValueHasStandoff, - KnoraApiV2Complex.DocumentFileValueHasPageCount -> KnoraBase.PageCount, - KnoraAdminV2.Lang -> KnoraAdmin.PreferredLanguage, - KnoraAdminV2.Projects -> KnoraAdmin.IsInProject, - KnoraAdminV2.SystemAdmin -> KnoraAdmin.IsInSystemAdminGroup, - KnoraAdminV2.ProjectClass -> KnoraAdmin.KnoraProject, - KnoraAdminV2.Shortname -> KnoraAdmin.ProjectShortname, - KnoraAdminV2.Longname -> KnoraAdmin.ProjectLongname, - KnoraAdminV2.Shortcode -> KnoraAdmin.ProjectShortcode, - KnoraAdminV2.Keywords -> KnoraAdmin.ProjectKeyword, - KnoraAdminV2.Logo -> KnoraAdmin.ProjectLogo, - KnoraAdminV2.GroupClass -> KnoraAdmin.UserGroup, - KnoraAdminV2.ProjectProperty -> KnoraAdmin.BelongsToProject, - KnoraAdminV2.SelfJoin -> KnoraAdmin.HasSelfJoinEnabled + KnoraApiV2Complex.DocumentFileValueHasPageCount -> KnoraBase.PageCount ) ) diff --git a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala index c317e3927f..73e7b0a16a 100644 --- a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala @@ -482,7 +482,7 @@ object SharedTestDataADM { shortcode = "0001", longname = Some("Anything Project"), description = Seq(StringLiteralV2(value = "Anything Project", language = None)), - keywords = Seq.empty[String], + keywords = Seq("things", "arbitrary test data"), logo = None, ontologies = Seq("http://www.knora.org/ontology/0001/anything", "http://www.knora.org/ontology/0001/something"), status = true, @@ -1598,6 +1598,18 @@ object SharedTestDataADM { | ?region knora-api:hasColor ?color . |}""".stripMargin + val gravsearchThingLinks: String = + """PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasOtherThing . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasOtherThing . + |}""".stripMargin + val createResourceWithValues: String = """{ | "@type" : "anything:Thing", diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala deleted file mode 100644 index 41d53f966f..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala +++ /dev/null @@ -1,1899 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.messages.v2.responder.ontologymessages - -import org.knora.webapi._ -import org.knora.webapi.messages.store.triplestoremessages.{OntologyLiteralV2, SmartIriLiteralV2, StringLiteralV2} -import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.{SmartIri, StringFormatter} - -/** - * Rules for converting `knora-admin` from the internal schema to the [[ApiV2Complex]] schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ -object KnoraAdminToApiV2ComplexTransformationRules extends OntologyTransformationRules { - private implicit val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies - - /** - * The metadata to be used for the transformed ontology. - */ - override val ontologyMetadata: OntologyMetadataV2 = OntologyMetadataV2( - ontologyIri = OntologyConstants.KnoraAdminV2.KnoraAdminOntologyIri.toSmartIri, - projectIri = Some(OntologyConstants.KnoraAdmin.SystemProject.toSmartIri), - label = Some("The knora-admin ontology in the complex schema") - ) - - private val Users: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Users, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.UsersResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "users" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The users returned in a UsersResponse." - ) - ) - ) - ) - - private val UserProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.UserProperty, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.UserResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "user" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The user returned in a UserResponse." - ) - ) - ) - ) - - private val ID: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ID, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "ID" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The ID of the enclosing object." - ) - ) - ) - ) - - private val Token: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Token, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "token" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The user's token." - ) - ) - ) - ) - - private val Ontologies: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Ontologies, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.ProjectClass), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "ontologies" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The ontologies attached to a project." - ) - ) - ) - ) - - private val SessionID: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.SessionID, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "session ID" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The user's session ID." - ) - ) - ) - ) - - private val ProjectDescription: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ProjectDescription, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.StringLiteral), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "description" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A description of a project." - ) - ) - ) - ) - - private val UsersResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UsersResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "users response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of users." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Users -> Cardinality.MayHaveMany - ) - ) - - private val UserResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UserResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "user response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a single user." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.UserProperty -> Cardinality.MustHaveOne - ) - ) - - private val ProjectsResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ProjectsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "projects response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of projects." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Projects -> Cardinality.MayHaveMany - ) - ) - - private val ProjectResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ProjectResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "project response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a single project." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ProjectProperty -> Cardinality.MustHaveOne - ) - ) - - private val GroupsResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.GroupsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "groups response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of groups." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Groups -> Cardinality.MayHaveMany - ) - ) - - private val ListsResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "lists response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of lists." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Lists -> Cardinality.MayHaveMany - ) - ) - - private val GroupResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.GroupResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "group response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a single group." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.GroupProperty -> Cardinality.MustHaveOne - ) - ) - - private val ListNodeInfo = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListNodeInfo, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list node info" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Information about a list node." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ID -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.Position -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.IsRootNode -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.HasRootNode -> Cardinality.MayHaveOne - ) - ) - - private val UpdateListInfoRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UpdateListInfoRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "update list info request" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to update information about a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ListIri -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany - ) - ) - - private val CreateListRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.CreateListRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "create list request" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to create a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany - ) - ) - - private val CreateChildNodeRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.CreateChildNodeRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "create child node request" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to create a child node in a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ParentNodeIri -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany - ) - ) - - private val ListResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response containing a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ListProperty -> Cardinality.MustHaveOne - ) - ) - - private val ListInfoResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListInfoResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list info response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response containing information about a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ListInfoProperty -> Cardinality.MustHaveOne - ) - ) - - private val ListNodeInfoResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListNodeInfoResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list node info response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response containing information about a list node." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.NodeInfo -> Cardinality.MustHaveOne - ) - ) - - private val ListClass = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListClass, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Represents a list." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ListInfoProperty -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.Children -> Cardinality.MayHaveMany - ) - ) - - private val ListNode = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ListNode, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list node" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A list node." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ID -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.Position -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.IsRootNode -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.HasRootNode -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Children -> Cardinality.MayHaveMany, - ) - ) - - private val Groups: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Groups, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.GroupClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "groups" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A collection of groups." - ) - ) - ) - ) - - private val ListInfoProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ListInfoProperty, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.ListNodeInfo), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list info" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides information about a list." - ) - ) - ) - ) - - private val NodeInfo: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.NodeInfo, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.ListNodeInfo), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list node info" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides information about a list node." - ) - ) - ) - ) - - private val ListProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ListProperty, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.ListClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides a list." - ) - ) - ) - ) - - private val ListIri: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ListIri, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "list IRI" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides a list IRI." - ) - ) - ) - ) - - private val ParentNodeIri: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ParentNodeIri, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "parent node IRI" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides a the IRI of a parent list node." - ) - ) - ) - ) - - private val Labels: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Labels, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.StringLiteral), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "labels" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The labels attached to the enclosing object." - ) - ) - ) - ) - - private val Comments: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Comments, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.StringLiteral), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "comments" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The comments attached to the enclosing object." - ) - ) - ) - ) - - private val Position: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Position, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Integer), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "position" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The position of a list node." - ) - ) - ) - ) - - private val IsRootNode: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.IsRootNode, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Boolean), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "is root node" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "True if this is the root node of a list." - ) - ) - ) - ) - - private val HasRootNode: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.HasRootNode, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "has root node" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The IRI of the root node of the list that this node belongs to." - ) - ) - ) - ) - - private val Children: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Children, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.ListNode), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "children" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The child nodes of this list node." - ) - ) - ) - ) - - private val Lists: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Lists, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.ListsResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.ListNodeInfo), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "lists" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A collection of lists." - ) - ) - ) - ) - - private val Members: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Members, - propertyType = OntologyConstants.Owl.ObjectProperty, - objectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "members" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The members of a group or project." - ) - ) - ) - ) - - private val GroupProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.GroupProperty, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.GroupResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.GroupClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "group" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A single group." - ) - ) - ) - ) - - private val KeywordsProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.KeywordsProperty, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.KeywordsResponse), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "keywords" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Project keywords." - ) - ) - ) - ) - - private val KeywordsResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.KeywordsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "keywords response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing project keywords." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.KeywordsProperty -> Cardinality.MayHaveMany - ) - ) - - private val MembersResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.MembersResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "members response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of group or project members." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Members -> Cardinality.MayHaveMany - ) - ) - - private val AdministrativePermissionsResponse: ReadClassInfoV2 = makeClass( - classIri = OntologyConstants.KnoraAdminV2.AdministrativePermissionsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permissions response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a collection of administrative permissions." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.AdministrativePermissions -> Cardinality.MayHaveMany - ) - ) - - private val AdministrativePermissionResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.AdministrativePermissionResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permission response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a single administrative permission." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.AdministrativePermissionProperty -> Cardinality.MustHaveOne - ) - ) - - private val UpdateUserRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UpdateUserRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "update user request" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to update a user." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Username -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Email -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.GivenName -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.FamilyName -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Lang -> Cardinality.MayHaveOne - ) - ) - - private val AdministrativePermissionProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.AdministrativePermissionProperty, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permission" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides a single administrative permission." - ) - ) - ) - ) - - private val AdministrativePermissions: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.AdministrativePermissions, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionsResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permissions" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "Provides a collection of administrative permissions." - ) - ) - ) - ) - - private val ForGroup: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ForGroup, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "for group" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The group that the permission applies to." - ) - ) - ) - ) - - private val ForProject: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ForProject, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "for project" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The project that the permission applies to." - ) - ) - ) - ) - - private val ForResourceClass: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ForResourceClass, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "for resource class" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The resource class that the permission applies to." - ) - ) - ) - ) - - private val ForProperty: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ForProperty, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "for property" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The property that the permission applies to." - ) - ) - ) - ) - - private val HasPermissions: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.HasPermissions, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionClass), - objectType = Some(OntologyConstants.KnoraAdminV2.Permission), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "has permissions" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The permissions granted by an AdministrativePermission." - ) - ) - ) - ) - - private val Permissions: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Permissions, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.UserClass), - objectType = Some(OntologyConstants.KnoraAdminV2.PermissionsData), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "permissions data" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A user's permissions data." - ) - ) - ) - ) - - private val AdministrativePermissionsPerProject: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.AdministrativePermissionsPerProject, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.PermissionsData), - objectType = Some(OntologyConstants.KnoraAdminV2.AdministrativePermissionsPerProjectCollectionType), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permissions per project" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A user's administrative permissions per project." - ) - ) - ) - ) - - private val GroupsPerProject: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.GroupsPerProject, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.PermissionsData), - objectType = Some(OntologyConstants.KnoraAdminV2.GroupsPerProjectCollectionType), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "groups per project" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A user's groups per project." - ) - ) - ) - ) - - private val Name: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Name, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "name" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The name of the enclosing object." - ) - ) - ) - ) - - private val AdditionalInformation: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.AdditionalInformation, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.Permission), - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "additional information" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "An IRI representing additional information about the permission." - ) - ) - ) - ) - - private val PermissionCode: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.PermissionCode, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.Permission), - objectType = Some(OntologyConstants.Xsd.Integer), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "permission code" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A permission's numeric permission code." - ) - ) - ) - ) - - private val Iri: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Iri, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "iri" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The IRI of the enclosing object." - ) - ) - ) - ) - - private val Settings: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Settings, - propertyType = OntologyConstants.Owl.ObjectProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettingsResponse), - objectType = Some(OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettings), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "settings" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A project's restricted view settings." - ) - ) - ) - ) - - private val Size: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Size, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettings), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "size" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The image size used in restricted image view in a project." - ) - ) - ) - ) - - private val Watermark: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Watermark, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettings), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "watermark" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The watermark used in restricted image view in a project." - ) - ) - ) - ) - - private val ProjectIri: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ProjectIri, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "project iri" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The IRI of a project." - ) - ) - ) - ) - - private val ProjectWithIriObj: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.ProjectWithIriObj, - propertyType = OntologyConstants.Owl.DatatypeProperty, - objectType = Some(OntologyConstants.Xsd.Uri), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "project iri" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The IRI of a project." - ) - ) - ) - ) - - private val ProjectRestrictedViewSettings = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettings, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "project restricted view settings" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A project's restricted view settings." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Size -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Watermark -> Cardinality.MayHaveOne - ) - ) - - private val ProjectRestrictedViewSettingsResponse = makeClass( - classIri = OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettingsResponse, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "project restricted view settings response" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A response providing a project's restricted view settings." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Settings -> Cardinality.MustHaveOne - ) - ) - - private val AdministrativePermissionClass = makeClass( - classIri = OntologyConstants.KnoraAdminV2.AdministrativePermissionClass, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "administrative permission" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "An administrative permission." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.ForGroup -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.ForProject -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.ForProperty -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.ForResourceClass -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.HasPermissions -> Cardinality.MustHaveSome, - OntologyConstants.KnoraAdminV2.Iri -> Cardinality.MustHaveOne - ) - ) - - private val Permission = makeClass( - classIri = OntologyConstants.KnoraAdminV2.Permission, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "permission" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A permission." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.AdditionalInformation -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.PermissionCode -> Cardinality.MayHaveOne - ) - ) - - private val PermissionsData = makeClass( - classIri = OntologyConstants.KnoraAdminV2.PermissionsData, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "permissions data" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A user's permissions data." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.AdministrativePermissionsPerProject -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.GroupsPerProject -> Cardinality.MayHaveOne - ) - ) - - private val CreateGroupRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.CreateGroupRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "create group" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to create a group." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.GroupDescription -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.ProjectWithIriObj -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.Status -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.SelfJoin -> Cardinality.MustHaveOne - ) - ) - - private val UpdateGroupRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UpdateGroupRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "update group" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to update a group." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.GroupDescription -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Status -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.SelfJoin -> Cardinality.MayHaveOne - ) - ) - - private val UpdateProjectRequest = makeClass( - classIri = OntologyConstants.KnoraAdminV2.UpdateProjectRequest, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "update project" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A request to update a project." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Shortname -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Longname -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.ProjectDescription -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.KeywordsProperty -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.Logo -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Status -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.SelfJoin -> Cardinality.MayHaveOne - ) - ) - - private val Value: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Value, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.StringLiteral), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "value" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The value of a string literal." - ) - ) - ) - ) - - private val Language: ReadPropertyInfoV2 = makeProperty( - propertyIri = OntologyConstants.KnoraAdminV2.Language, - propertyType = OntologyConstants.Owl.DatatypeProperty, - subjectType = Some(OntologyConstants.KnoraAdminV2.StringLiteral), - objectType = Some(OntologyConstants.Xsd.String), - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "language" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "The language of a string literal." - ) - ) - ) - ) - - private val StringLiteral = makeClass( - classIri = OntologyConstants.KnoraAdminV2.StringLiteral, - predicates = Seq( - makePredicate( - predicateIri = OntologyConstants.Rdfs.Label, - objectsWithLang = Map( - LanguageCodes.EN -> "string literal" - ) - ), - makePredicate( - predicateIri = OntologyConstants.Rdfs.Comment, - objectsWithLang = Map( - LanguageCodes.EN -> "A string with an optional language tag." - ) - ) - ), - directCardinalities = Map( - OntologyConstants.KnoraAdminV2.Value -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.Language -> Cardinality.MayHaveOne - ) - ) - - /** - * Properties to remove from the ontology before converting it to the target schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ - override val internalPropertiesToRemove: Set[SmartIri] = Set( - OntologyConstants.KnoraAdmin.ProjectRestrictedViewSize, - OntologyConstants.KnoraAdmin.ProjectRestrictedViewWatermark, - OntologyConstants.KnoraAdmin.BelongsToInstitution, - OntologyConstants.KnoraAdmin.ForProject, - OntologyConstants.KnoraAdmin.ForGroup, - OntologyConstants.KnoraAdmin.ForResourceClass, - OntologyConstants.KnoraAdmin.ForProperty, - OntologyConstants.KnoraAdmin.GroupName, - OntologyConstants.KnoraAdmin.IsInGroup - ).map(_.toSmartIri) - - /** - * Classes to remove from the ontology before converting it to the target schema. - */ - override val internalClassesToRemove: Set[SmartIri] = Set( - OntologyConstants.KnoraAdmin.Institution, - OntologyConstants.KnoraAdmin.Permission, - OntologyConstants.KnoraAdmin.AdministrativePermission, - OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission - ).map(_.toSmartIri) - - /** - * Cardinalities to add to the User class. - */ - private val UserCardinalities = Map( - OntologyConstants.KnoraAdminV2.ID -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Password -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Token -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.SessionID -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.SystemAdmin -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Groups -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.Permissions -> Cardinality.MustHaveOne - ) - - /** - * Cardinalities to add to the Group class. - */ - private val GroupCardinalities = Map( - OntologyConstants.KnoraAdminV2.ID -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Name -> Cardinality.MustHaveOne, - OntologyConstants.KnoraAdminV2.SelfJoin -> Cardinality.MustHaveOne - ) - - /** - * Cardinalities to add to the Project class. - */ - private val ProjectCardinalities = Map( - OntologyConstants.KnoraAdminV2.ID -> Cardinality.MayHaveOne, - OntologyConstants.KnoraAdminV2.Ontologies -> Cardinality.MayHaveMany, - OntologyConstants.KnoraAdminV2.SelfJoin -> Cardinality.MustHaveOne - ) - - /** - * After the ontology has been converted to the target schema, these cardinalities must be - * added to the specified classes. - */ - override val externalCardinalitiesToAdd: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = Map( - OntologyConstants.KnoraAdminV2.UserClass -> UserCardinalities, - OntologyConstants.KnoraAdminV2.GroupClass -> GroupCardinalities, - OntologyConstants.KnoraAdminV2.ProjectClass -> ProjectCardinalities - ).map { - case (classIri, cardinalities) => - classIri.toSmartIri -> cardinalities.map { - case (propertyIri, cardinality) => - propertyIri.toSmartIri -> Cardinality.KnoraCardinalityInfo(cardinality) - } - } - - /** - * Classes that need to be added to the ontology after converting it to the target schema. - */ - override val externalClassesToAdd: Map[SmartIri, ReadClassInfoV2] = Set( - UpdateUserRequest, - UsersResponse, - UserResponse, - ProjectsResponse, - ProjectResponse, - UpdateProjectRequest, - GroupsResponse, - GroupResponse, - ListsResponse, - ListNodeInfo, - ListNode, - ListResponse, - ListClass, - CreateListRequest, - CreateChildNodeRequest, - UpdateListInfoRequest, - ListInfoResponse, - ListNodeInfoResponse, - CreateGroupRequest, - UpdateGroupRequest, - AdministrativePermissionsResponse, - AdministrativePermissionResponse, - AdministrativePermissionClass, - Permission, - PermissionsData, - MembersResponse, - KeywordsResponse, - ProjectRestrictedViewSettings, - ProjectRestrictedViewSettingsResponse, - StringLiteral - ).map { - classInfo => classInfo.entityInfoContent.classIri -> classInfo - }.toMap - - /** - * Properties that need to be added to the ontology after converting it to the target schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ - override val externalPropertiesToAdd: Map[SmartIri, ReadPropertyInfoV2] = Set( - Users, - UserProperty, - ID, - Token, - SessionID, - Ontologies, - ProjectDescription, - AdministrativePermissionProperty, - AdministrativePermissions, - Permissions, - AdministrativePermissionsPerProject, - GroupsPerProject, - ForGroup, - ForProject, - ForResourceClass, - ForProperty, - HasPermissions, - Name, - AdditionalInformation, - PermissionCode, - Iri, - Groups, - Members, - Lists, - ListInfoProperty, - ListProperty, - ListIri, - ParentNodeIri, - NodeInfo, - GroupProperty, - KeywordsProperty, - Settings, - Size, - Watermark, - ProjectIri, - ProjectWithIriObj, - Value, - Language, - Labels, - Comments, - Position, - IsRootNode, - HasRootNode, - Children - ).map { - propertyInfo => propertyInfo.entityInfoContent.propertyIri -> propertyInfo - }.toMap - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Convenience functions for building ontology entities, to make the code above more concise. - - /** - * Makes a [[PredicateInfoV2]]. - * - * @param predicateIri the IRI of the predicate. - * @param objects the non-language-specific objects of the predicate. - * @param objectsWithLang the language-specific objects of the predicate. - * @return a [[PredicateInfoV2]]. - */ - private def makePredicate(predicateIri: IRI, - objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2], - objectsWithLang: Map[String, String] = Map.empty[String, String]): PredicateInfoV2 = { - PredicateInfoV2( - predicateIri = predicateIri.toSmartIri, - objects = objects ++ objectsWithLang.map { - case (lang, str) => StringLiteralV2(str, Some(lang)) - } - ) - } - - /** - * Makes a [[ReadPropertyInfoV2]]. - * - * @param propertyIri the IRI of the property. - * @param propertyType the type of the property (owl:ObjectProperty, owl:DatatypeProperty, or rdf:Property). - * @param predicates the property's predicates. - * @param subjectType the required type of the property's subject. - * @param objectType the required type of the property's object. - * @return a [[ReadPropertyInfoV2]]. - */ - private def makeProperty(propertyIri: IRI, - propertyType: IRI, - predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], - subjectType: Option[IRI] = None, - objectType: Option[IRI] = None): ReadPropertyInfoV2 = { - val propTypePred = makePredicate( - predicateIri = OntologyConstants.Rdf.Type, - objects = Seq(SmartIriLiteralV2(propertyType.toSmartIri)) - ) - - val maybeSubjectTypePred = subjectType.map { - subjType => - makePredicate( - predicateIri = OntologyConstants.KnoraApiV2Complex.SubjectType, - objects = Seq(SmartIriLiteralV2(subjType.toSmartIri)) - ) - } - - val maybeObjectTypePred = objectType.map { - objType => - makePredicate( - predicateIri = OntologyConstants.KnoraApiV2Complex.ObjectType, - objects = Seq(SmartIriLiteralV2(objType.toSmartIri)) - ) - } - - val predsWithTypes = predicates ++ maybeSubjectTypePred ++ maybeObjectTypePred :+ propTypePred - - ReadPropertyInfoV2( - entityInfoContent = PropertyInfoContentV2( - propertyIri = propertyIri.toSmartIri, - ontologySchema = ApiV2Complex, - predicates = predsWithTypes.map { - pred => pred.predicateIri -> pred - }.toMap - ) - ) - } - - /** - * Makes a [[ReadClassInfoV2]]. - * - * @param classIri the IRI of the class. - * @param predicates the predicates of the class. - * @param directCardinalities the direct cardinalities of the class. - * @return a [[ReadClassInfoV2]]. - */ - private def makeClass(classIri: IRI, - predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], - directCardinalities: Map[IRI, Cardinality.Value] = Map.empty[IRI, Cardinality.Value]): ReadClassInfoV2 = { - val rdfType = OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( - predicateIri = OntologyConstants.Rdf.Type.toSmartIri, - objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) - ) - - ReadClassInfoV2( - entityInfoContent = ClassInfoContentV2( - classIri = classIri.toSmartIri, - predicates = predicates.map { - pred => pred.predicateIri -> pred - }.toMap + rdfType, - directCardinalities = directCardinalities.map { - case (propertyIri, cardinality) => propertyIri.toSmartIri -> KnoraCardinalityInfo(cardinality) - }, - subClassOf = Set.empty, - ontologySchema = ApiV2Complex - ), - allBaseClasses = Set.empty - ) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala index 1a423adeff..19aa679af2 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala @@ -1406,7 +1406,9 @@ object InputOntologyV2 { val entityIris: Iterable[SmartIri] = classes.values.map(_.classIri) ++ properties.values.map(_.propertyIri) ++ individuals.values.map(_.individualIri) - val entityIrisInWrongOntology = entityIris.filter(_.getOntologyFromEntity != externalOntologyIri) + val entityIrisInWrongOntology: Set[SmartIri] = entityIris.filter { + entityIri => entityIri.getOntologyFromEntity != externalOntologyIri + }.toSet if (entityIrisInWrongOntology.nonEmpty) { throw BadRequestException(s"One or more entities are not in ontology $externalOntologyIri: ${entityIrisInWrongOntology.mkString(", ")}") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala index 914fc0f953..3c7950d097 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala @@ -78,18 +78,9 @@ object OntologyTransformationRules { * @return the appropriate [[OntologyTransformationRules]]. */ def getTransformationRules(ontologyIri: SmartIri, targetSchema: ApiV2Schema): OntologyTransformationRules = { - // If this is the admin ontology, use its transformation rules. - if (ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri) { - targetSchema match { - case ApiV2Simple => throw BadRequestException(s"The knora-admin API is not available in the simple schema") - case ApiV2Complex => KnoraAdminToApiV2ComplexTransformationRules - } - } else { - // Otherwise, use the knora-base transformation rules. - targetSchema match { - case ApiV2Simple => KnoraBaseToApiV2SimpleTransformationRules - case ApiV2Complex => KnoraBaseToApiV2ComplexTransformationRules - } + targetSchema match { + case ApiV2Simple => KnoraBaseToApiV2SimpleTransformationRules + case ApiV2Complex => KnoraBaseToApiV2ComplexTransformationRules } } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala index 6caf269adc..9bc6c40433 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala @@ -1135,8 +1135,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon case (acc, classIri) => // Is this class IRI hard-coded in the requested schema? if (KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd.contains(classIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.contains(classIri) || - KnoraAdminToApiV2ComplexTransformationRules.externalClassesToAdd.contains(classIri)) { + KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.contains(classIri)) { // Yes, so it's available. acc } else { @@ -1163,8 +1162,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon case (acc, propertyIri) => // Is this property IRI hard-coded in the requested schema? if (KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd.contains(propertyIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.contains(propertyIri) || - KnoraAdminToApiV2ComplexTransformationRules.externalPropertiesToAdd.contains(propertyIri)) { + KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.contains(propertyIri)) { // Yes, so it's available. acc } else { @@ -1204,12 +1202,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // See if any of the requested entities are hard-coded for knora-api. hardCodedExternalClassesAvailable: Map[SmartIri, ReadClassInfoV2] = KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd.filterKeys(classIris) ++ - KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.filterKeys(classIris) ++ - KnoraAdminToApiV2ComplexTransformationRules.externalClassesToAdd.filterKeys(classIris) + KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.filterKeys(classIris) hardCodedExternalPropertiesAvailable: Map[SmartIri, ReadPropertyInfoV2] = KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd.filterKeys(propertyIris) ++ - KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.filterKeys(propertyIris) ++ - KnoraAdminToApiV2ComplexTransformationRules.externalPropertiesToAdd.filterKeys(propertyIris) + KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.filterKeys(propertyIris) // Convert the remaining external entity IRIs to internal ones. diff --git a/webapi/src/main/scala/org/knora/webapi/routing/ClientApiRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/ClientApiRoute.scala index b32038b3ab..3c2432c01c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/ClientApiRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/ClientApiRoute.scala @@ -26,18 +26,17 @@ import akka.http.scaladsl.model.{HttpEntity, HttpResponse, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout -import org.knora.webapi._ import org.knora.webapi.routing.admin.AdminClientApi import org.knora.webapi.routing.v2.V2ClientApi -import org.knora.webapi.util.FileUtil -import org.knora.webapi.util.clientapi._ +import org.knora.webapi.util.{FileUtil, TestDataFileContent} import scala.concurrent.Future import scala.concurrent.duration._ +/** + * Generates test data for testing client API code. + */ class ClientApiRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { - private val TYPESCRIPT: String = "typescript" - override implicit val timeout: Timeout = 20111.millis private val apiDefs = Seq( @@ -50,48 +49,18 @@ class ClientApiRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi */ override def knoraApiPath: Route = { - path("clientapi" / Segment) { target: String => + path("clientapitest") { get { // Respond with a Content-Disposition header specifying the filename of the generated Zip file. - respondWithHeader(`Content-Disposition`(ContentDispositionTypes.attachment, Map("filename" -> s"$target-client-api.zip"))) { + respondWithHeader(`Content-Disposition`(ContentDispositionTypes.attachment, Map("filename" -> s"client-api-test-data.zip"))) { requestContext => - // Construct the generator back end for the specified target. - val generatorBackEnd: GeneratorBackEnd = target match { - case TYPESCRIPT => new TypeScriptBackEnd - case _ => throw ClientApiGenerationException(s"Unknown target: $target") - } - - val params: Map[String, String] = requestContext.request.uri.query().toMap - - val httpResponseFuture: Future[HttpResponse] = for { - requestingUser <- getUserADM(requestContext) - - // Construct the generator front end. - generatorFrontEnd = new GeneratorFrontEnd(routeData, requestingUser) - - // Get the class definitions from the front end. - backEndInputFutures: Seq[Future[ClientApiBackendInput]] = apiDefs.map { - apiDef => - for { - classDefs <- generatorFrontEnd.getClientClassDefs(apiDef) - } yield ClientApiBackendInput(apiDef, classDefs) - } - - backEndInputSeq: Seq[ClientApiBackendInput] <- Future.sequence(backEndInputFutures) - backEndInputs = backEndInputSeq.toSet - - // Generate source code. - sourceCode: Set[SourceCodeFileContent] = generatorBackEnd.generateClientSourceCode( - apis = backEndInputs, - params = params - ) - - // Generate test data. - testDataPerApi: Seq[Set[SourceCodeFileContent]] <- Future.sequence(apiDefs.map(_.getTestData(testDataDirectoryPath = Seq("test-data")))) - sourceCodeWithTestData: Set[SourceCodeFileContent] = sourceCode ++ testDataPerApi.flatten + val httpResponseFuture = for { + // Generate client test data. + testDataPerApi: Seq[Set[TestDataFileContent]] <- Future.sequence(apiDefs.map(_.getTestData(testDataDirectoryPath = Seq("test-data")))) + testData: Set[TestDataFileContent] = testDataPerApi.flatten.toSet - // Generate a Zip file from the source code. - zipFileBytes = generateZipFile(sourceCodeWithTestData) + // Generate a Zip file from the test data. + zipFileBytes = generateZipFile(testData) } yield HttpResponse( status = StatusCodes.OK, entity = HttpEntity(bytes = zipFileBytes) @@ -109,9 +78,9 @@ class ClientApiRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi * @param sourceCode the generated source code. * @return a byte array representing the ZIP file. */ - private def generateZipFile(sourceCode: Set[SourceCodeFileContent]): Array[Byte] = { + private def generateZipFile(sourceCode: Set[TestDataFileContent]): Array[Byte] = { val zipFileContents: Map[String, Array[Byte]] = sourceCode.map { - fileContent: SourceCodeFileContent => + fileContent: TestDataFileContent => fileContent.filePath.toString -> fileContent.text.getBytes(StandardCharsets.UTF_8) }.toMap diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/AdminClientApi.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/AdminClientApi.scala index b4a41fc1f1..9358d62611 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/AdminClientApi.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/AdminClientApi.scala @@ -19,24 +19,14 @@ package org.knora.webapi.routing.admin -import org.knora.webapi.OntologyConstants import org.knora.webapi.routing.KnoraRouteData -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi._ -import org.knora.webapi.util.{SmartIri, StringFormatter} +import org.knora.webapi.util.{ClientApi, ClientEndpoint} /** - * Represents the structure of generated client library code for the admin API. + * Represents the structure of generated test data for the admin API. */ class AdminClientApi(routeData: KnoraRouteData) extends ClientApi { - implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - /** - * The serialisation format used by this [[ClientApi]]. - */ - override val serialisationFormat: ApiSerialisationFormat = Json - /** * The endpoints in this [[ClientApi]]. */ @@ -49,138 +39,7 @@ class AdminClientApi(routeData: KnoraRouteData) extends ClientApi { ) /** - * The name of this [[ClientApi]]. - */ - override val name: String = "AdminEndpoint" - - /** - * The directory name to be used for this API's code. + * The directory name to be used for this API's test data. */ override val directoryName: String = "admin" - - /** - * The URL path of this [[ClientApi]]. - */ - override val urlPath: String = "/admin" - - /** - * A description of this [[ClientApi]]. - */ - override val description: String = "A client API for administering Knora." - - /** - * A map of class IRIs to their read-only properties. - */ - override val classesWithReadOnlyProperties: Map[SmartIri, Set[SmartIri]] = Map( - OntologyConstants.KnoraAdminV2.UserClass -> Set( - OntologyConstants.KnoraAdminV2.Token, - OntologyConstants.KnoraAdminV2.SessionID, - OntologyConstants.KnoraAdminV2.Groups, - OntologyConstants.KnoraAdminV2.Projects, - OntologyConstants.KnoraAdminV2.Permissions - ), - OntologyConstants.KnoraAdminV2.GroupClass -> Set( - OntologyConstants.KnoraAdminV2.ProjectProperty - ), - OntologyConstants.KnoraAdminV2.ProjectClass -> Set( - OntologyConstants.KnoraAdminV2.Ontologies - ) - ).map { - case (classIri, propertyIris) => - classIri.toSmartIri -> propertyIris.map(_.toSmartIri) - } - - /** - * A map of class IRIs to IRIs of optional set properties. Such properties have cardinality 0-n, and should - * be made optional in generated code. - */ - override val classesWithOptionalSetProperties: Map[SmartIri, Set[SmartIri]] = Map( - OntologyConstants.KnoraAdminV2.UpdateProjectRequest -> Set( - OntologyConstants.KnoraAdminV2.KeywordsProperty, - OntologyConstants.KnoraAdminV2.ProjectDescription - ) - ).map { - case (classIri, propertyIris) => - classIri.toSmartIri -> propertyIris.map(_.toSmartIri) - } - - /** - * A set of IRIs of classes that represent API requests and that therefore do not need `Stored*` - * subclasses. - */ - override val requestClasses: Set[SmartIri] = Set( - OntologyConstants.KnoraAdminV2.UpdateUserRequest, - OntologyConstants.KnoraAdminV2.CreateGroupRequest, - OntologyConstants.KnoraAdminV2.UpdateProjectRequest, - OntologyConstants.KnoraAdminV2.UpdateGroupRequest, - OntologyConstants.KnoraAdminV2.CreateChildNodeRequest, - OntologyConstants.KnoraAdminV2.CreateListRequest, - OntologyConstants.KnoraAdminV2.UpdateListInfoRequest - ).map(_.toSmartIri) - - /** - * A set of IRIs of classes that are always read-only and that therefore do not need `Stored*` - * or `Read*` subclasses. - */ - override val readOnlyClasses: Set[SmartIri] = Set( - OntologyConstants.KnoraAdminV2.ListClass, - OntologyConstants.KnoraAdminV2.ListNodeInfo, - OntologyConstants.KnoraAdminV2.ListNode - ).map(_.toSmartIri) - - /** - * A set of IRIs of classes that represent API responses. - */ - override val responseClasses: Set[SmartIri] = Set( - OntologyConstants.KnoraAdminV2.UserResponse, - OntologyConstants.KnoraAdminV2.UsersResponse, - OntologyConstants.KnoraAdminV2.GroupResponse, - OntologyConstants.KnoraAdminV2.GroupsResponse, - OntologyConstants.KnoraAdminV2.ProjectResponse, - OntologyConstants.KnoraAdminV2.ProjectsResponse, - OntologyConstants.KnoraAdminV2.MembersResponse, - OntologyConstants.KnoraAdminV2.KeywordsResponse, - OntologyConstants.KnoraAdminV2.AdministrativePermissionResponse, - OntologyConstants.KnoraAdminV2.AdministrativePermissionsResponse, - OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettingsResponse, - OntologyConstants.KnoraAdminV2.ListsResponse, - OntologyConstants.KnoraAdminV2.ListResponse, - OntologyConstants.KnoraAdminV2.ListInfoResponse, - OntologyConstants.KnoraAdminV2.ListNodeInfoResponse - ).map(_.toSmartIri) - - /** - * A set of property IRIs that are used for the unique IDs of objects. - */ - override val idProperties: Set[SmartIri] = Set( - OntologyConstants.KnoraAdminV2.ID, - OntologyConstants.KnoraAdminV2.Iri, - OntologyConstants.KnoraAdminV2.ListIri - ).map(_.toSmartIri) - - /** - * A map of class IRIs to maps of property IRIs to non-standard names that those properties must have - * in those classes. Needed only for JSON, and only if two different properties should have the same name in - * different classes. `JsonInstanceInspector` also needs to know about these. - */ - override lazy val propertyNames: Map[SmartIri, Map[SmartIri, String]] = AdminClientApi.propertyNames } - -object AdminClientApi { - def propertyNames(implicit stringFormatter: StringFormatter): Map[SmartIri, Map[SmartIri, String]] = Map( - OntologyConstants.KnoraAdminV2.CreateGroupRequest -> Map( - OntologyConstants.KnoraAdminV2.ProjectWithIriObj -> "project", - OntologyConstants.KnoraAdminV2.GroupDescription -> "description" - ), - OntologyConstants.KnoraAdminV2.UpdateGroupRequest -> Map(OntologyConstants.KnoraAdminV2.GroupDescription -> "description"), - OntologyConstants.KnoraAdminV2.ProjectClass -> Map(OntologyConstants.KnoraAdminV2.ProjectDescription -> "description"), - OntologyConstants.KnoraAdminV2.UpdateProjectRequest -> Map(OntologyConstants.KnoraAdminV2.ProjectDescription -> "description"), - OntologyConstants.KnoraAdminV2.GroupClass -> Map(OntologyConstants.KnoraAdminV2.GroupDescription -> "description") - ).map { - case (classIri, propertyMap) => - classIri.toSmartIri -> propertyMap.map { - case (propertyIri, propertyName) => - propertyIri.toSmartIri -> propertyName - } - } -} \ No newline at end of file diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala index bf71bcbc06..8c16900957 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala @@ -32,10 +32,8 @@ import io.swagger.annotations._ import javax.ws.rs.Path import org.knora.webapi.messages.admin.responder.groupsmessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.EndpointFunctionDSL._ -import org.knora.webapi.util.clientapi._ -import org.knora.webapi.{BadRequestException, OntologyConstants, SharedTestDataADM} +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} +import org.knora.webapi.{BadRequestException, SharedTestDataADM} import scala.concurrent.{ExecutionContext, Future} @@ -55,34 +53,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi import GroupsRouteADM._ - /** - * The name of this [[ClientEndpoint]]. - */ - override val name: String = "GroupsEndpoint" - /** * The directory name to be used for this endpoint's code. */ override val directoryName: String = "groups" - /** - * The URL path of this [[ClientEndpoint]]. - */ - override val urlPath: String = "/groups" - - /** - * A description of this [[ClientEndpoint]]. - */ - override val description: String = "An endpoint for working with Knora groups." - - // Classes used in client function definitions. - - private val GroupsResponse = classRef(OntologyConstants.KnoraAdminV2.GroupsResponse.toSmartIri) - private val GroupResponse = classRef(OntologyConstants.KnoraAdminV2.GroupResponse.toSmartIri) - private val MembersResponse = classRef(OntologyConstants.KnoraAdminV2.MembersResponse.toSmartIri) - private val CreateGroupRequest = classRef(OntologyConstants.KnoraAdminV2.CreateGroupRequest.toSmartIri) - private val UpdateGroupRequest = classRef(OntologyConstants.KnoraAdminV2.UpdateGroupRequest.toSmartIri) - private val groupIri = SharedTestDataADM.imagesReviewerGroup.id private val groupIriEnc = java.net.URLEncoder.encode(groupIri, "utf-8") @@ -107,16 +82,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val getGroupsFunction: ClientFunction = - "getGroups" description "Returns a list of all groups." params() doThis { - httpGet(BasePath) - } returns GroupsResponse - - private def getGroupsTestResponse: Future[SourceCodeFileContent] = { + private def getGroupsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(baseApiUrl + GroupsBasePathString) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-groups-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-groups-response"), text = responseStr ) } @@ -148,20 +118,10 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val createGroupFunction: ClientFunction = - "createGroup" description "Creates a group." params ( - "group" description "The group to be created." paramType CreateGroupRequest - ) doThis { - httpPost( - path = BasePath, - body = Some(arg("group")) - ) - } returns GroupResponse - - private def createGroupTestRequest: Future[SourceCodeFileContent] = { + private def createGroupTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-group-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-group-request"), text = SharedTestDataADM.createGroupRequest ) ) @@ -190,18 +150,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val getGroupByIriFunction: ClientFunction = - "getGroupByIri" description "Gets a group by IRI." params ( - "iri" description "The IRI of the group." paramType UriDatatype - ) doThis { - httpGet(arg("iri")) - } returns GroupResponse - - private def getGroupByIriTestResponse: Future[SourceCodeFileContent] = { + private def getGroupByIriTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$GroupsBasePathString/$groupIriEnc") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-group-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-group-response"), text = responseStr ) } @@ -245,21 +198,10 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val updateGroupFunction: ClientFunction = - "updateGroup" description "Updates a group." params( - "iri" description "The IRI of the group to be updated." paramType UriDatatype, - "groupInfo" description "The group information to be updated." paramType UpdateGroupRequest - ) doThis { - httpPut( - path = arg("iri"), - body = Some(arg("groupInfo")) - ) - } returns GroupResponse - - private def updateGroupTestRequest: Future[SourceCodeFileContent] = { + private def updateGroupTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-group-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-group-request"), text = SharedTestDataADM.updateGroupRequest ) ) @@ -307,23 +249,10 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val changeGroupStatusFunction: ClientFunction = - "updateGroupStatus" description "Updates the status of a group." params( - "iri" description "The IRI of the group to be updated." paramType UriDatatype, - "status" description "The new status of the group." paramType BooleanDatatype - ) doThis { - httpPut( - path = arg("iri") / str("status"), - body = Some(json( - "status" -> arg("status") - )) - ) - } returns GroupResponse - - private def changeGroupStatusTestRequest: Future[SourceCodeFileContent] = { + private def changeGroupStatusTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("change-group-status-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("change-group-status-request"), text = SharedTestDataADM.changeGroupStatusRequest ) ) @@ -357,16 +286,6 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val deleteGroupFunction: ClientFunction = - "deleteGroup" description "Deletes a group. This method does not actually delete a group, but sets the status to false." params ( - "iri" description "The IRI of the group." paramType UriDatatype - ) doThis { - httpDelete( - path = arg("iri") - ) - } returns GroupResponse - - /** * Gets members of single group. */ @@ -390,18 +309,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi } } - private val getGroupMembersFunction: ClientFunction = - "getGroupMembers" description "Gets the members of a group." params ( - "iri" description "The IRI of the group." paramType UriDatatype - ) doThis { - httpGet(arg("iri") / str("members")) - } returns MembersResponse - - private def getGroupMembersTestResponse: Future[SourceCodeFileContent] = { + private def getGroupMembersTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$GroupsBasePathString/$groupIriEnc/members") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-group-members-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-group-members-response"), text = responseStr ) } @@ -412,19 +324,6 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi override def knoraApiPath: Route = getGroups ~ createGroup ~ getGroupByIri ~ updateGroup ~ changeGroupStatus ~ deleteGroup ~ getGroupMembers - /** - * The functions defined by this [[ClientEndpoint]]. - */ - override val functions: Seq[ClientFunction] = Seq( - getGroupsFunction, - createGroupFunction, - getGroupByIriFunction, - updateGroupFunction, - changeGroupStatusFunction, - deleteGroupFunction, - getGroupMembersFunction - ) - /** * Returns test data for this endpoint. * @@ -432,7 +331,7 @@ class GroupsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi */ override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { Future.sequence { Set( getGroupsTestResponse, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala index 3eee0bb0dc..14aa7f16e4 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala @@ -34,9 +34,7 @@ import javax.ws.rs.Path import org.knora.webapi._ import org.knora.webapi.messages.admin.responder.listsmessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.EndpointFunctionDSL._ -import org.knora.webapi.util.clientapi._ +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} import scala.concurrent.{ExecutionContext, Future} @@ -54,35 +52,11 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit import ListsRouteADM._ - /** - * The name of this [[ClientEndpoint]]. - */ - override val name: String = "ListsEndpoint" - /** * The directory name to be used for this endpoint's code. */ override val directoryName: String = "lists" - /** - * The URL path of this [[ClientEndpoint]]. - */ - override val urlPath: String = "/lists" - - /** - * A description of this [[ClientEndpoint]]. - */ - override val description: String = "An endpoint for working with Knora lists." - - // Classes used in client function definitions. - - private val ListsResponse = classRef(OntologyConstants.KnoraAdminV2.ListsResponse.toSmartIri) - private val CreateListRequest = classRef(OntologyConstants.KnoraAdminV2.CreateListRequest.toSmartIri) - private val ListResponse = classRef(OntologyConstants.KnoraAdminV2.ListResponse.toSmartIri) - private val ListInfoResponse = classRef(OntologyConstants.KnoraAdminV2.ListInfoResponse.toSmartIri) - private val ListNodeInfoResponse = classRef(OntologyConstants.KnoraAdminV2.ListNodeInfoResponse.toSmartIri) - private val UpdateListInfoRequest = classRef(OntologyConstants.KnoraAdminV2.UpdateListInfoRequest.toSmartIri) - private val CreateChildNodeRequest = classRef(OntologyConstants.KnoraAdminV2.CreateChildNodeRequest.toSmartIri) private val anythingList = URLEncoder.encode("http://rdfh.ch/lists/0001/treeList", "UTF-8") private val anythingListNode = URLEncoder.encode("http://rdfh.ch/lists/0001/treeList01", "UTF-8") @@ -120,29 +94,11 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getListsFunction: ClientFunction = - "getLists" description "Returns a list of lists." params() doThis { - httpGet(BasePath) - } returns ListsResponse - - - // #getListsInProjectFunction - private val getListsInProjectFunction: ClientFunction = - "getListsInProject" description "Returns a list of lists in a project." params ( - "projectIri" description "The IRI of the project." paramType UriDatatype - ) doThis { - httpGet( - path = BasePath, - params = Seq("projectIri" -> arg("projectIri")) - ) - } returns ListsResponse - // #getListsInProjectFunction - - private def getListsTestResponse: Future[SourceCodeFileContent] = { + private def getListsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(baseApiUrl + ListsBasePathString) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-lists-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-lists-response"), text = responseStr ) } @@ -180,20 +136,10 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val createListFunction: ClientFunction = - "createList" description "Creates a list." params ( - "listInfo" description "Information about the list to be created." paramType CreateListRequest - ) doThis { - httpPost( - path = BasePath, - body = Some(arg("listInfo")) - ) - } returns ListResponse - - private def createListTestRequest: Future[SourceCodeFileContent] = { + private def createListTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-list-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-list-request"), text = SharedTestDataADM.createListRequest ) ) @@ -225,20 +171,11 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getListFunction: ClientFunction = - "getList" description "Gets a list." params ( - "iri" description "The IRI of the list." paramType UriDatatype - ) doThis { - httpGet( - path = arg("iri") - ) - } returns ListResponse - - private def getListTestResponse: Future[SourceCodeFileContent] = { + private def getListTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/$anythingList") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-list-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-list-response"), text = responseStr ) } @@ -282,20 +219,10 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val updateListInfoFunction: ClientFunction = - "updateListInfo" description "Updates information about a list." params ( - "listInfo" description "Information about the list to be created." paramType UpdateListInfoRequest - ) doThis { - httpPut( - path = argMember("listInfo", "listIri"), - body = Some(arg("listInfo")) - ) - } returns ListInfoResponse - - private def updateListInfoTestRequest: Future[SourceCodeFileContent] = { + private def updateListInfoTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-list-info-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-list-info-request"), text = SharedTestDataADM.updateListInfoRequest("http://rdfh.ch/lists/0001/treeList01") ) ) @@ -340,20 +267,10 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val createChildNodeFunction: ClientFunction = - "createChildNode" description "Creates a child node in a list." params ( - "node" description "The node to be created." paramType CreateChildNodeRequest - ) doThis { - httpPost( - path = argMember("node", "parentNodeIri"), - body = Some(arg("node")) - ) - } returns ListNodeInfoResponse - - private def createChildNodeTestRequest: Future[SourceCodeFileContent] = { + private def createChildNodeTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-child-node-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-child-node-request"), text = SharedTestDataADM.addChildListNodeRequest( parentNodeIri = "http://rdfh.ch/lists/0001/treeList01", name = "abc123", @@ -393,20 +310,11 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getListInfoFunction: ClientFunction = - "getListInfo" description "Returns information about a list." params ( - "iri" description "The IRI of the list." paramType UriDatatype - ) doThis { - httpGet( - path = str("infos") / arg("iri") - ) - } returns ListInfoResponse - - private def getListInfoTestResponse: Future[SourceCodeFileContent] = { + private def getListInfoTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/infos/$anythingList") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-list-info-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-list-info-response"), text = responseStr ) } @@ -468,44 +376,21 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getListNodeInfoFunction: ClientFunction = - "getListNodeInfo" description "Returns information about a list node." params ( - "iri" description "The IRI of the node." paramType UriDatatype - ) doThis { - httpGet( - path = str("nodes") / arg("iri") - ) - } returns ListNodeInfoResponse - - private def getListNodeInfoTestResponse: Future[SourceCodeFileContent] = { + private def getListNodeInfoTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/nodes/$anythingListNode") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-list-node-info-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-list-node-info-response"), text = responseStr ) } - /** - * The functions defined by this [[ClientEndpoint]]. - */ - override val functions: Seq[ClientFunction] = Seq( - getListsFunction, - getListsInProjectFunction, - createListFunction, - getListFunction, - updateListInfoFunction, - createChildNodeFunction, - getListInfoFunction, - getListNodeInfoFunction - ) - /** * Returns test data for this endpoint. * * @return a set of test data files to be used for testing this endpoint. */ - override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { Future.sequence { Set( getListsTestResponse, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala index d774074f81..cdb9ca24fc 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala @@ -30,15 +30,13 @@ import io.swagger.annotations.Api import javax.ws.rs.Path import org.knora.webapi.messages.admin.responder.permissionsmessages.{AdministrativePermissionForProjectGroupGetRequestADM, PermissionType} import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.EndpointFunctionDSL._ -import org.knora.webapi.util.clientapi._ +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} import org.knora.webapi.{OntologyConstants, SharedTestDataADM} import scala.concurrent.{ExecutionContext, Future} object PermissionsRouteADM { - val PermissionsBasePath = PathMatcher("admin" / "permissions") + val PermissionsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "permissions") val PermissionsBasePathString: String = "/admin/permissions" } @@ -48,30 +46,11 @@ class PermissionsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeDat import PermissionsRouteADM._ - /** - * The name of this [[ClientEndpoint]]. - */ - override val name: String = "PermissionsEndpoint" - /** * The directory name to be used for this endpoint's code. */ override val directoryName: String = "permissions" - /** - * The URL path of of this [[ClientEndpoint]]. - */ - override val urlPath: String = "/permissions" - - /** - * A description of this [[ClientEndpoint]]. - */ - override val description: String = "An endpoint for working with Knora permissions." - - // Classes used in client function definitions. - - private val AdministrativePermissionResponse = classRef(OntologyConstants.KnoraAdminV2.AdministrativePermissionResponse.toSmartIri) - private val projectIri: String = URLEncoder.encode(SharedTestDataADM.imagesProject.id, "utf-8") private val groupIri: String = URLEncoder.encode(OntologyConstants.KnoraAdmin.ProjectMember, "utf-8") @@ -101,54 +80,21 @@ class PermissionsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeDat } } - private val getAdministrativePermissionFunction: ClientFunction = - "getAdministrativePermission" description "Gets the administrative permission for a project and group." params( - "projectIri" description "The project IRI." paramType UriDatatype, - "groupIri" description "The group IRI." paramType UriDatatype, - ) doThis { - httpGet( - path = arg("projectIri") / arg("groupIri") - ) - } returns AdministrativePermissionResponse - - /* - // Commented out because the 'projectType' parameter is ignored. - - private val getAdministrativePermissionByTypeFunction: ClientFunction = - "getAdministrativePermissionByType" description "Gets the administrative permission for a project and group, specifying a permission type." params( - "projectIri" description "The project IRI." paramType UriDatatype, - "groupIri" description "The group IRI." paramType UriDatatype, - "permissionType" description "The permission type." paramType StringDatatype - ) doThis { - httpGet( - path = arg("projectIri") / arg("groupIri"), - params = Seq("permissionType" -> arg("permissionType")) - ) - } returns AdministrativePermissionResponse - */ - - private def getAdministrativePermissionTestResponse: Future[SourceCodeFileContent] = { + private def getAdministrativePermissionTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$PermissionsBasePathString/$projectIri/$groupIri")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-administrative-permission-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-administrative-permission-response"), text = responseStr ) } - /** - * The functions defined by this [[ClientEndpoint]]. - */ - override val functions: Seq[ClientFunction] = Seq( - getAdministrativePermissionFunction - ) - /** * Returns test data for this endpoint. * * @return a set of test data files to be used for testing this endpoint. */ - override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { Future.sequence { Set( getAdministrativePermissionTestResponse diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala index 2e3758aab8..a0ded1a7b5 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala @@ -40,10 +40,8 @@ import javax.ws.rs.Path import org.knora.webapi.annotation.ApiMayChange import org.knora.webapi.messages.admin.responder.projectsmessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.EndpointFunctionDSL._ -import org.knora.webapi.util.clientapi._ -import org.knora.webapi.{BadRequestException, IRI, OntologyConstants, SharedTestDataADM} +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} +import org.knora.webapi.{BadRequestException, IRI, SharedTestDataADM} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try @@ -59,38 +57,13 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) import ProjectsRouteADM._ - /** - * The name of this [[ClientEndpoint]]. - */ - override val name: String = "ProjectsEndpoint" - /** * The directory name to be used for this endpoint's code. */ override val directoryName: String = "projects" - /** - * The URL path of this [[ClientEndpoint]]. - */ - override val urlPath: String = "/projects" - - /** - * A description of this [[ClientEndpoint]]. - */ - override val description: String = "An endpoint for working with Knora projects." - - // Classes used in client function definitions. - - private val Project = classRef(OntologyConstants.KnoraAdminV2.ProjectClass.toSmartIri) - private val ProjectsResponse = classRef(OntologyConstants.KnoraAdminV2.ProjectsResponse.toSmartIri) - private val ProjectResponse = classRef(OntologyConstants.KnoraAdminV2.ProjectResponse.toSmartIri) - private val UpdateProjectRequest = classRef(OntologyConstants.KnoraAdminV2.UpdateProjectRequest.toSmartIri) - private val KeywordsResponse = classRef(OntologyConstants.KnoraAdminV2.KeywordsResponse.toSmartIri) - private val MembersResponse = classRef(OntologyConstants.KnoraAdminV2.MembersResponse.toSmartIri) - private val ProjectRestrictedViewSettingsResponse = classRef(OntologyConstants.KnoraAdminV2.ProjectRestrictedViewSettingsResponse.toSmartIri) - private val imagesProjectIriEnc = URLEncoder.encode(SharedTestDataADM.imagesProject.id, "utf-8") - private val incunabulaIriEnc = URLEncoder.encode(SharedTestDataADM.incunabulaProject.id, "utf-8") + private val anythingProjectIriEnc = URLEncoder.encode(SharedTestDataADM.anythingProject.id, "utf-8") /** * Returns the route. @@ -129,16 +102,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectsFunction: ClientFunction = - "getProjects" description "Returns a list of all projects." params() doThis { - httpGet(BasePath) - } returns ProjectsResponse - - private def getProjectsTestResponse: Future[SourceCodeFileContent] = { + private def getProjectsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(baseApiUrl + ProjectsBasePathString) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-projects-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-projects-response"), text = responseStr ) } @@ -175,20 +143,10 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val createProjectFunction: ClientFunction = - "createProject" description "Creates a project." params ( - "project" description "The project to be created." paramType Project - ) doThis { - httpPost( - path = BasePath, - body = Some(arg("project")) - ) - } returns ProjectResponse - - private def createProjectTestRequest: Future[SourceCodeFileContent] = { + private def createProjectTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-project-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-project-request"), text = SharedTestDataADM.createProjectRequest ) ) @@ -212,17 +170,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getKeywordsFunction: ClientFunction = - "getKeywords" description "Gets all the unique keywords for all projects." params() doThis { - httpGet(str("Keywords")) - } returns KeywordsResponse - - - private def getKeywordsTestResponse: Future[SourceCodeFileContent] = { + private def getKeywordsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/Keywords") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-keywords-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-keywords-response"), text = responseStr ) } @@ -247,18 +199,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectKeywordsFunction: ClientFunction = - "getProjectKeywords" description "Gets all the keywords for a project." params ( - "iri" description "The IRI of the project." paramType UriDatatype - ) doThis { - httpGet(str("iri") / arg("iri") / str("Keywords")) - } returns KeywordsResponse - - private def getProjectKeywordsTestResponse: Future[SourceCodeFileContent] = { + private def getProjectKeywordsTestResponse: Future[TestDataFileContent] = { for { - responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$incunabulaIriEnc/Keywords") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-project-keywords-response"), + responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$anythingProjectIriEnc/Keywords") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-project-keywords-response"), text = responseStr ) } @@ -285,30 +230,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectFunction: ClientFunction = - "getProject" description "Gets a project by a property." params( - "property" description "The name of the property by which the project is identified." paramType enum("iri", "shortname", "shortcode"), - "value" description "The value of the property by which the project is identified." paramType StringDatatype - ) doThis { - httpGet(arg("property") / arg("value")) - } returns ProjectResponse - - private def getProjectTestResponse: Future[SourceCodeFileContent] = { + private def getProjectTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$imagesProjectIriEnc") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-project-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-project-response"), text = responseStr ) } - private val getProjectByIriFunction: ClientFunction = - "getProjectByIri" description "Gets a project by IRI." params ( - "iri" description "The IRI of the project." paramType UriDatatype - ) doThis { - getProjectFunction withArgs(str("iri"), arg("iri") as StringDatatype) - } returns ProjectResponse - /** * returns a single project identified through shortname. */ @@ -331,13 +261,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectByShortnameFunction: ClientFunction = - "getProjectByShortname" description "Gets a project by shortname." params ( - "shortname" description "The shortname of the project." paramType StringDatatype - ) doThis { - getProjectFunction withArgs(str("shortname"), arg("shortname")) - } returns ProjectResponse - /** * returns a single project identified through shortcode. */ @@ -360,13 +283,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectByShortcodeFunction: ClientFunction = - "getProjectByShortcode" description "Gets a project by shortcode." params ( - "shortcode" description "The shortcode of the project." paramType StringDatatype - ) doThis { - getProjectFunction withArgs(str("shortcode"), arg("shortcode")) - } returns ProjectResponse - /** * update a project identified by iri */ @@ -398,21 +314,10 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val updateProjectFunction: ClientFunction = - "updateProject" description "Updates a project." params( - "iri" description "The IRI of the project to be updated." paramType UriDatatype, - "projectInfo" description "The project info to be updated." paramType UpdateProjectRequest - ) doThis { - httpPut( - path = str("iri") / arg("iri"), - body = Some(arg("projectInfo")) - ) - } returns ProjectResponse - - private def updateProjectTestRequest: Future[SourceCodeFileContent] = { + private def updateProjectTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-project-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-project-request"), text = SharedTestDataADM.updateProjectRequest ) ) @@ -446,15 +351,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val deleteProjectFunction: ClientFunction = - "deleteProject" description "Deletes a project. This method does not actually delete a project, but sets the status to false." params ( - "iri" description "The project IRI." paramType UriDatatype - ) doThis { - httpDelete( - path = str("iri") / arg("iri") - ) - } returns ProjectResponse - /** * API MAY CHANGE: returns all members part of a project identified through iri */ @@ -479,30 +375,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectMembersFunction: ClientFunction = - "getProjectMembers" description "Gets a project's members by a property." params( - "property" description "The name of the property by which the project is identified." paramType enum("iri", "shortname", "shortcode"), - "value" description "The value of the property by which the project is identified." paramType StringDatatype - ) doThis { - httpGet(arg("property") / arg("value") / str("members")) - } returns MembersResponse - - private def getProjectMembersTestResponse: Future[SourceCodeFileContent] = { + private def getProjectMembersTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$imagesProjectIriEnc/members") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-project-members-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-project-members-response"), text = responseStr ) } - private val getProjectMembersByIriFunction: ClientFunction = - "getProjectMembersByIri" description "Gets the members of a project by IRI." params ( - "iri" description "The IRI of the project." paramType UriDatatype - ) doThis { - getProjectMembersFunction withArgs(str("iri"), arg("iri") as StringDatatype) - } returns MembersResponse - /** * API MAY CHANGE: returns all members part of a project identified through shortname */ @@ -526,13 +407,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectMembersByShortnameFunction: ClientFunction = - "getProjectMembersByShortname" description "Gets a project's members by shortname." params ( - "shortname" description "The shortname of the project." paramType StringDatatype - ) doThis { - getProjectMembersFunction withArgs(str("shortname"), arg("shortname")) - } returns MembersResponse - /** * API MAY CHANGE: returns all members part of a project identified through shortcode */ @@ -557,13 +431,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectMembersByShortcodeFunction: ClientFunction = - "getProjectMembersByShortcode" description "Gets a project's members by shortcode." params ( - "shortcode" description "The shortcode of the project." paramType StringDatatype - ) doThis { - getProjectMembersFunction withArgs(str("shortcode"), arg("shortcode")) - } returns MembersResponse - /** * API MAY CHANGE: returns all admin members part of a project identified through iri */ @@ -587,30 +454,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectAdminMembersFunction: ClientFunction = - "getProjectAdminMembers" description "Gets a project's admin members by a property." params( - "property" description "The name of the property by which the project is identified." paramType enum("iri", "shortname", "shortcode"), - "value" description "The value of the property by which the project is identified." paramType StringDatatype - ) doThis { - httpGet(arg("property") / arg("value") / str("admin-members")) - } returns MembersResponse - - private def getProjectAdminMembersTestResponse: Future[SourceCodeFileContent] = { + private def getProjectAdminMembersTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$imagesProjectIriEnc/admin-members") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-project-admin-members-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-project-admin-members-response"), text = responseStr ) } - private val getProjectAdminMembersByIriFunction: ClientFunction = - "getProjectAdminMembersByIri" description "Gets the admin members of a project by IRI." params ( - "iri" description "The IRI of the project." paramType UriDatatype - ) doThis { - getProjectAdminMembersFunction withArgs(str("iri"), arg("iri") as StringDatatype) - } returns MembersResponse - /** * API MAY CHANGE: returns all admin members part of a project identified through shortname */ @@ -634,13 +486,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectAdminMembersByShortnameFunction: ClientFunction = - "getProjectAdminMembersByShortname" description "Gets a project's admin members by shortname." params ( - "shortname" description "The shortname of the project." paramType StringDatatype - ) doThis { - getProjectAdminMembersFunction withArgs(str("shortname"), arg("shortname")) - } returns MembersResponse - /** * API MAY CHANGE: returns all admin members part of a project identified through shortcode */ @@ -664,13 +509,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectAdminMembersByShortcodeFunction: ClientFunction = - "getProjectAdminMembersByShortcode" description "Gets a project's admin members by shortcode." params ( - "shortcode" description "The shortcode of the project." paramType StringDatatype - ) doThis { - getProjectAdminMembersFunction withArgs(str("shortcode"), arg("shortcode")) - } returns MembersResponse - /** * Returns the project's restricted view settings identified through IRI. */ @@ -693,30 +531,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectRestrictedViewSettingsFunction: ClientFunction = - "getProjectRestrictedViewSettings" description "Gets a project's restricted view settings by a property." params( - "property" description "The name of the property by which the project is identified." paramType enum("iri", "shortname", "shortcode"), - "value" description "The value of the property by which the project is identified." paramType StringDatatype - ) doThis { - httpGet(arg("property") / arg("value") / str("RestrictedViewSettings")) - } returns ProjectRestrictedViewSettingsResponse - - private def getProjectRestrictedViewSettingsTestResponse: Future[SourceCodeFileContent] = { + private def getProjectRestrictedViewSettingsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ProjectsBasePathString/iri/$imagesProjectIriEnc/RestrictedViewSettings") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-project-restricted-view-settings-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-project-restricted-view-settings-response"), text = responseStr ) } - private val getProjectRestrictedViewSettingByIriFunction: ClientFunction = - "getProjectRestrictedViewSettingByIri" description "Gets a project's restricted view settings by IRI." params ( - "iri" description "The IRI of the project." paramType UriDatatype - ) doThis { - getProjectRestrictedViewSettingsFunction withArgs(str("iri"), arg("iri") as StringDatatype) - } returns ProjectRestrictedViewSettingsResponse - /** * Returns the project's restricted view settings identified through shortname. */ @@ -740,13 +563,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectRestrictedViewSettingByShortnameFunction: ClientFunction = - "getProjectRestrictedViewSettingByShortname" description "Gets a project's restricted view settings by shortname." params ( - "shortname" description "The shortname of the project." paramType StringDatatype - ) doThis { - getProjectRestrictedViewSettingsFunction withArgs(str("shortname"), arg("shortname") as StringDatatype) - } returns ProjectRestrictedViewSettingsResponse - /** * Returns the project's restricted view settings identified through shortcode. */ @@ -768,13 +584,6 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private val getProjectRestrictedViewSettingByShortcodeFunction: ClientFunction = - "getProjectRestrictedViewSettingByShortcode" description "Gets a project's restricted view settings by shortcode." params ( - "shortcode" description "The shortcode of the project." paramType StringDatatype - ) doThis { - getProjectRestrictedViewSettingsFunction withArgs(str("shortcode"), arg("shortcode") as StringDatatype) - } returns ProjectRestrictedViewSettingsResponse - /** * Returns all ontologies, data, and configuration belonging to a project. */ @@ -804,40 +613,12 @@ class ProjectsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - /** - * The functions defined by this [[ClientEndpoint]]. - */ - override val functions: Seq[ClientFunction] = Seq( - getProjectsFunction, - createProjectFunction, - getKeywordsFunction, - getProjectKeywordsFunction, - updateProjectFunction, - deleteProjectFunction, - getProjectFunction, - getProjectByIriFunction, - getProjectByShortnameFunction, - getProjectByShortcodeFunction, - getProjectMembersFunction, - getProjectMembersByIriFunction, - getProjectMembersByShortnameFunction, - getProjectMembersByShortcodeFunction, - getProjectAdminMembersFunction, - getProjectAdminMembersByIriFunction, - getProjectAdminMembersByShortnameFunction, - getProjectAdminMembersByShortcodeFunction, - getProjectRestrictedViewSettingsFunction, - getProjectRestrictedViewSettingByIriFunction, - getProjectRestrictedViewSettingByShortnameFunction, - getProjectRestrictedViewSettingByShortcodeFunction - ) - /** * Returns test data for this endpoint. * * @return a set of test data files to be used for testing this endpoint. */ - override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { Future.sequence { Set( getProjectsTestResponse, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala index e7de23f020..796b7410e9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala @@ -41,7 +41,7 @@ class StoreRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def knoraApiPath = Route { + override def knoraApiPath: Route = Route { path("admin" / "store") { get { requestContext => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala index cd2f841b65..59f6a64126 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala @@ -35,10 +35,8 @@ import org.knora.webapi.annotation.ApiMayChange import org.knora.webapi.messages.admin.responder.usersmessages.UsersADMJsonProtocol._ import org.knora.webapi.messages.admin.responder.usersmessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.EndpointFunctionDSL._ -import org.knora.webapi.util.clientapi._ -import org.knora.webapi.{BadRequestException, KnoraSystemInstances, OntologyConstants, SharedTestDataADM} +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} +import org.knora.webapi.{BadRequestException, KnoraSystemInstances, SharedTestDataADM} import scala.concurrent.{ExecutionContext, Future} @@ -57,35 +55,11 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit import UsersRouteADM._ - /** - * The name of this [[ClientEndpoint]]. - */ - override val name: String = "UsersEndpoint" - /** * The directory name to be used for this endpoint's code. */ override val directoryName: String = "users" - /** - * The URL path of this [[ClientEndpoint]]. - */ - override val urlPath: String = "/users" - - /** - * A description of this [[ClientEndpoint]]. - */ - override val description: String = "An endpoint for working with Knora users." - - // Classes used in client function definitions. - - private val UsersResponse = classRef(OntologyConstants.KnoraAdminV2.UsersResponse.toSmartIri) - private val UserResponse = classRef(OntologyConstants.KnoraAdminV2.UserResponse.toSmartIri) - private val ProjectsResponse = classRef(OntologyConstants.KnoraAdminV2.ProjectsResponse.toSmartIri) - private val GroupsResponse = classRef(OntologyConstants.KnoraAdminV2.GroupsResponse.toSmartIri) - private val User = classRef(OntologyConstants.KnoraAdminV2.UserClass.toSmartIri) - private val UpdateUserRequest = classRef(OntologyConstants.KnoraAdminV2.UpdateUserRequest.toSmartIri) - private val anythingUser1IriEnc = URLEncoder.encode(SharedTestDataADM.anythingUser1.id, "UTF-8") private val multiUserIriEnc = URLEncoder.encode(SharedTestDataADM.multiuserUser.id, "UTF-8") @@ -124,17 +98,11 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getUsersFunction: ClientFunction = - "getUsers" description "Returns a list of all users." params() doThis { - httpGet(BasePath) - } returns UsersResponse - - - private def getUsersTestResponse: Future[SourceCodeFileContent] = { + private def getUsersTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(baseApiUrl + UsersBasePathString) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-users-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-users-response"), text = responseStr ) } @@ -171,22 +139,10 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - // #createUserFunction - private val createUserFunction: ClientFunction = - "createUser" description "Creates a user." params ( - "user" description "The user to be created." paramType User - ) doThis { - httpPost( - path = BasePath, - body = Some(arg("user")) - ) - } returns UserResponse - // #createUserFunction - - private def createUserTestRequest: Future[SourceCodeFileContent] = { + private def createUserTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-user-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-user-request"), text = SharedTestDataADM.createUserRequest ) ) @@ -212,34 +168,15 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - // #getUserFunction - private val getUserFunction: ClientFunction = - "getUser" description "Gets a user by a property." params( - "property" description "The name of the property by which the user is identified." paramType enum("iri", "email", "username"), - "value" description "The value of the property by which the user is identified." paramType StringDatatype - ) doThis { - httpGet(arg("property") / arg("value")) - } returns UserResponse - // #getUserFunction - - private def getUserTestResponse: Future[SourceCodeFileContent] = { + private def getUserTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$UsersBasePathString/iri/$anythingUser1IriEnc") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-user-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-user-response"), text = responseStr ) } - // #getUserByIriFunction - private val getUserByIriFunction: ClientFunction = - "getUserByIri" description "Gets a user by IRI." params ( - "iri" description "The IRI of the user." paramType UriDatatype - ) doThis { - getUserFunction withArgs(str("iri"), arg("iri") as StringDatatype) - } returns UserResponse - // #getUserByIriFunction - /** * return a single user identified by email */ @@ -260,13 +197,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getUserByEmailFunction: ClientFunction = - "getUserByEmail" description "Gets a user by email address." params ( - "email" description "The email address of the user." paramType StringDatatype - ) doThis { - getUserFunction withArgs(str("email"), arg("email")) - } returns UserResponse - /** * return a single user identified by username */ @@ -287,13 +217,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getUserByUsernameFunction: ClientFunction = - "getUserByUsername" description "Gets a user by username." params ( - "username" description "The username of the user." paramType StringDatatype - ) doThis { - getUserFunction withArgs(str("username"), arg("username")) - } returns UserResponse - /** * API MAY CHANGE: Change existing user's basic information. */ @@ -331,21 +254,10 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val updateUserBasicInformationFunction: ClientFunction = - "updateUserBasicInformation" description "Updates an existing user's basic information." params( - "iri" description "The IRI of the user to be updated." paramType UriDatatype, - "userInfo" description "The user information to be updated." paramType UpdateUserRequest - ) doThis { - httpPut( - path = str("iri") / arg("iri") / str("BasicUserInformation"), - body = Some(arg("userInfo")) - ) - } returns UserResponse - - private def updateUserTestRequest: Future[SourceCodeFileContent] = { + private def updateUserTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-user-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-user-request"), text = SharedTestDataADM.updateUserRequest ) ) @@ -388,27 +300,10 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - // #updateUserPasswordFunction - private val updateUserPasswordFunction: ClientFunction = - "updateUserPassword" description "Updates a user's password." params( - "iri" description "The IRI of the user to be updated." paramType UriDatatype, - "requesterPassword" description "The requesting user's current password." paramType StringDatatype, - "newPassword" description "The specified user's new password." paramType StringDatatype - ) doThis { - httpPut( - path = str("iri") / arg("iri") / str("Password"), - body = Some(json( - "requesterPassword" -> arg("requesterPassword"), - "newPassword" -> arg("newPassword") - )) - ) - } returns UserResponse - // #updateUserPasswordFunction - - private def updateUserPasswordTestRequest: Future[SourceCodeFileContent] = { + private def updateUserPasswordTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-user-password-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-user-password-request"), text = SharedTestDataADM.changeUserPasswordRequest ) ) @@ -451,21 +346,10 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val updateUserStatusFunction: ClientFunction = - "updateUserStatus" description "Updates a user's status." params( - "iri" description "The user's IRI." paramType UriDatatype, - "status" description "The user's new status." paramType BooleanDatatype - ) doThis { - httpPut( - path = str("iri") / arg("iri") / str("Status"), - body = Some(json("status" -> arg("status"))) - ) - } returns UserResponse - - private def updateUserStatusTestRequest: Future[SourceCodeFileContent] = { + private def updateUserStatusTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-user-status-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-user-status-request"), text = SharedTestDataADM.changeUserStatusRequest ) ) @@ -505,15 +389,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val deleteUserFunction: ClientFunction = - "deleteUser" description "Deletes a user. This method does not actually delete a user, but sets the status to false." params ( - "iri" description "The IRI of the user to be deleted." paramType UriDatatype - ) doThis { - httpDelete( - path = str("iri") / arg("iri") - ) - } returns UserResponse - /** * API MAY CHANGE: Change user's SystemAdmin membership. */ @@ -551,21 +426,10 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val updateUserSystemAdminMembershipFunction: ClientFunction = - "updateUserSystemAdminMembership" description "Updates a user's SystemAdmin membership." params ( - "iri" description "The IRI of the user to be updated." paramType UriDatatype, - "systemAdmin" description "True if the user should be a system admin" paramType BooleanDatatype - ) doThis { - httpPut( - path = str("iri") / arg("iri") / str("SystemAdmin"), - body = Some(json("systemAdmin" -> arg("systemAdmin"))) - ) - } returns UserResponse - - private def updateUserSystemAdminMembershipTestRequest: Future[SourceCodeFileContent] = { + private def updateUserSystemAdminMembershipTestRequest: Future[TestDataFileContent] = { FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-user-system-admin-membership-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-user-system-admin-membership-request"), text = SharedTestDataADM.changeUserSystemAdminMembershipRequest ) ) @@ -598,18 +462,11 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getUserProjectMembershipsFunction: ClientFunction = - "getUserProjectMemberships" description "Gets a user's project memberships." params ( - "iri" description "The IRI of the user." paramType UriDatatype - ) doThis { - httpGet(path = str("iri") / arg("iri") / str("project-memberships")) - } returns ProjectsResponse - - private def getUserProjectMembershipsResponse: Future[SourceCodeFileContent] = { + private def getUserProjectMembershipsResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$UsersBasePathString/iri/$multiUserIriEnc/project-memberships") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-user-project-memberships-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-user-project-memberships-response"), text = responseStr ) } @@ -643,16 +500,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val addUserToProjectMembershipFunction: ClientFunction = - "addUserToProjectMembership" description "Adds a user to a project." params( - "userIri" description "The user's IRI." paramType UriDatatype, - "projectIri" description "The project's IRI." paramType UriDatatype - ) doThis { - httpPost( - path = str("iri") / arg("userIri") / str("project-memberships") / arg("projectIri") - ) - } returns UserResponse - /** * API MAY CHANGE: remove user from project (and all groups belonging to this project) */ @@ -683,16 +530,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val removeUserFromProjectMembershipFunction: ClientFunction = - "removeUserFromProjectMembership" description "Removes a user from a project." params( - "userIri" description "The user's IRI." paramType UriDatatype, - "projectIri" description "The project's IRI." paramType UriDatatype - ) doThis { - httpDelete( - path = str("iri") / arg("userIri") / str("project-memberships") / arg("projectIri") - ) - } returns UserResponse - /** * API MAY CHANGE: get user's project admin memberships */ @@ -720,13 +557,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val getUserProjectAdminMembershipsFunction: ClientFunction = - "getUserProjectAdminMemberships" description "Gets a user's project admin memberships." params ( - "iri" description "The user's IRI." paramType UriDatatype - ) doThis { - httpGet(path = str("iri") / arg("iri") / str("project-admin-memberships")) - } returns ProjectsResponse - /** * API MAY CHANGE: add user to project admin */ @@ -757,16 +587,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val addUserToProjectAdminMembershipFunction: ClientFunction = - "addUserToProjectAdminMembership" description "Makes a user a project administrator." params( - "userIri" description "The IRI of the user." paramType UriDatatype, - "projectIri" description "The IRI of the project." paramType UriDatatype - ) doThis { - httpPost( - path = str("iri") / arg("userIri") / str("project-admin-memberships") / arg("projectIri") - ) - } returns UserResponse - /** * API MAY CHANGE: remove user from project admin membership */ @@ -796,16 +616,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val removeUserFromProjectAdminMembershipFunction: ClientFunction = - "removeUserFromProjectAdminMembership" description "Removes a user's project administrator status." params( - "userIri" description "The IRI of the user." paramType UriDatatype, - "projectIri" description "The IRI of the project." paramType UriDatatype - ) doThis { - httpDelete( - path = str("iri") / arg("userIri") / str("project-admin-memberships") / arg("projectIri") - ) - } returns UserResponse - /** * API MAY CHANGE: get user's group memberships */ @@ -833,20 +643,11 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - // #getUserGroupMembershipsFunction - private val getUserGroupMembershipsFunction: ClientFunction = - "getUserGroupMemberships" description "Gets a user's group memberships." params ( - "iri" description "The user's IRI." paramType UriDatatype - ) doThis { - httpGet(path = str("iri") / arg("iri") / str("group-memberships")) - } returns GroupsResponse - // #getUserGroupMembershipsFunction - - private def getUserGroupMembershipsTestResponse: Future[SourceCodeFileContent] = { + private def getUserGroupMembershipsTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$UsersBasePathString/iri/$anythingUser1IriEnc/group-memberships") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("get-user-group-memberships-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("get-user-group-memberships-response"), text = responseStr ) } @@ -880,16 +681,6 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val addUserToGroupMembershipFunction: ClientFunction = - "addUserToGroupMembership" description "Adds a user to a group." params( - "userIri" description "The IRI of the user." paramType UriDatatype, - "groupIri" description "The IRI of the group." paramType UriDatatype - ) doThis { - httpPost( - path = str("iri") / arg("userIri") / str("group-memberships") / arg("groupIri") - ) - } returns UserResponse - /** * API MAY CHANGE: remove user from group */ @@ -919,48 +710,12 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private val removeUserFromGroupMembershipFunction: ClientFunction = - "removeUserFromGroupMembership" description "Removes a user from a project." params( - "userIri" description "The IRI of the user." paramType UriDatatype, - "groupIri" description "The IRI of the group." paramType UriDatatype - ) doThis { - httpDelete( - path = str("iri") / arg("userIri") / str("group-memberships") / arg("groupIri") - ) - } returns UserResponse - - /** - * The functions defined by this [[ClientEndpoint]]. - */ - override val functions: Seq[ClientFunction] = Seq( - getUsersFunction, - getUserFunction, - getUserByIriFunction, - getUserByEmailFunction, - getUserByUsernameFunction, - getUserGroupMembershipsFunction, - getUserProjectMembershipsFunction, - getUserProjectAdminMembershipsFunction, - createUserFunction, - updateUserBasicInformationFunction, - updateUserStatusFunction, - updateUserPasswordFunction, - addUserToGroupMembershipFunction, - removeUserFromGroupMembershipFunction, - addUserToProjectMembershipFunction, - removeUserFromProjectMembershipFunction, - addUserToProjectAdminMembershipFunction, - removeUserFromProjectAdminMembershipFunction, - updateUserSystemAdminMembershipFunction, - deleteUserFunction - ) - /** * Returns test data for this endpoint. * * @return a set of test data files to be used for testing this endpoint. */ - override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { Future.sequence { Set( getUsersTestResponse, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala index e556544876..da890f102c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala @@ -29,7 +29,7 @@ import akka.stream.ActorMaterializer import org.knora.webapi._ import org.knora.webapi.messages.v2.responder.listsmessages.{ListGetRequestV2, NodeGetRequestV2} import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV2} -import org.knora.webapi.util.clientapi.{ClientEndpoint, ClientFunction, SourceCodeFileContent, SourceCodeFilePath} +import org.knora.webapi.util.{ClientEndpoint, TestDataFileContent, TestDataFilePath} import scala.concurrent.{ExecutionContext, Future} @@ -38,12 +38,8 @@ import scala.concurrent.{ExecutionContext, Future} */ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator with ClientEndpoint { - // Definitions for ClientEndpoint - override val name: String = "ListsEndpoint" + // Directory name for generated test data override val directoryName: String = "lists" - override val urlPath: String = "lists" - override val description: String = "An endpoint for working with Knora lists." - override val functions: Seq[ClientFunction] = Seq.empty /** * Returns the route. @@ -77,15 +73,15 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with "othertreelist" -> SharedTestDataADM.otherTreeList ) - private def getListTestResponses: Future[Set[SourceCodeFileContent]] = { - val responseFutures: Iterable[Future[SourceCodeFileContent]] = testLists.map { + private def getListTestResponses: Future[Set[TestDataFileContent]] = { + val responseFutures: Iterable[Future[TestDataFileContent]] = testLists.map { case (filename, listIri) => val encodedListIri = URLEncoder.encode(listIri, "UTF-8") for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl/v2/lists/$encodedListIri")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(filename), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(filename), text = responseStr ) } @@ -114,11 +110,11 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with } } - private def getNodeTestResponse: Future[SourceCodeFileContent] = { + private def getNodeTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl/v2/node/${URLEncoder.encode(SharedTestDataADM.treeListNode, "UTF-8")}")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("listnode"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("listnode"), text = responseStr ) } @@ -126,7 +122,7 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { for { testLists <- getListTestResponses testNode <- getNodeTestResponse diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala index f3068e3053..d2e930a0f3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala @@ -31,9 +31,8 @@ import org.knora.webapi._ import org.knora.webapi.messages.v2.responder.ontologymessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV2} import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.SmartIri -import org.knora.webapi.util.clientapi.{ClientEndpoint, ClientFunction, SourceCodeFileContent, SourceCodeFilePath} import org.knora.webapi.util.jsonld.{JsonLDDocument, JsonLDUtil} +import org.knora.webapi.util.{ClientEndpoint, SmartIri, TestDataFileContent, TestDataFilePath} import scala.concurrent.{ExecutionContext, Future} @@ -49,12 +48,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) import OntologiesRouteV2._ - // Definitions for ClientEndpoint - override val name: String = "OntologiesEndpoint" + // Directory name for generated test data override val directoryName: String = "ontologies" - override val urlPath: String = "ontologies" - override val description: String = "An endpoint for working with Knora ontologies." - override val functions: Seq[ClientFunction] = Seq.empty private val ALL_LANGUAGES = "allLanguages" private val LAST_MODIFICATION_DATE = "lastModificationDate" @@ -140,11 +135,11 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def getOntologyMetadataTestResponse: Future[SourceCodeFileContent] = { + private def getOntologyMetadataTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$OntologiesBasePathString/metadata")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("all-ontology-metadata"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("all-ontology-metadata"), text = responseStr ) } @@ -251,15 +246,15 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) /** * Provides JSON-LD responses to requests for values, for use in tests of generated client code. */ - private def getOntologyTestResponses: Future[Set[SourceCodeFileContent]] = { - val responseFutures: Iterable[Future[SourceCodeFileContent]] = testOntologies.map { + private def getOntologyTestResponses: Future[Set[TestDataFileContent]] = { + val responseFutures: Iterable[Future[TestDataFileContent]] = testOntologies.map { case (filename, ontologyIri) => val encodedOntologyIri = URLEncoder.encode(ontologyIri, "UTF-8") for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$OntologiesBasePathString/allentities/$encodedOntologyIri")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(filename), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(filename), text = responseStr ) } @@ -740,10 +735,10 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { for { - ontologyResponses: Set[SourceCodeFileContent] <- getOntologyTestResponses - ontologyMetadataResponses: SourceCodeFileContent <- getOntologyMetadataTestResponse + ontologyResponses: Set[TestDataFileContent] <- getOntologyTestResponses + ontologyMetadataResponses: TestDataFileContent <- getOntologyMetadataTestResponse } yield ontologyResponses + ontologyMetadataResponses } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala index 552fcef977..0a75b88ca8 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala @@ -34,9 +34,8 @@ import org.knora.webapi.messages.v2.responder.resourcemessages._ import org.knora.webapi.messages.v2.responder.searchmessages.SearchResourcesByProjectAndClassRequestV2 import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV2} import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.{ClientEndpoint, ClientFunction, SourceCodeFileContent, SourceCodeFilePath} import org.knora.webapi.util.jsonld.{JsonLDDocument, JsonLDUtil} -import org.knora.webapi.util.{SmartIri, StringFormatter} +import org.knora.webapi.util.{ClientEndpoint, SmartIri, StringFormatter, TestDataFileContent, TestDataFilePath} import scala.concurrent.{ExecutionContext, Future} @@ -52,12 +51,8 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) import ResourcesRouteV2._ - // Definitions for ClientEndpoint - override val name: String = "ResourcesEndpoint" + // Directory name for generated test data override val directoryName: String = "resources" - override val urlPath: String = "resources" - override val description: String = "An endpoint for working with Knora resources." - override val functions: Seq[ClientFunction] = Seq.empty private val Text_Property = "textProperty" private val Mapping_Iri = "mappingIri" @@ -110,19 +105,19 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def createResourceTestRequests: Future[Set[SourceCodeFileContent]] = { + private def createResourceTestRequests: Future[Set[TestDataFileContent]] = { FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-resource-with-values-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-resource-with-values-request"), text = SharedTestDataADM.createResourceWithValues ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-resource-with-custom-creation-date"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-resource-with-custom-creation-date"), text = SharedTestDataADM.createResourceWithCustomCreationDate(Instant.parse("2019-01-09T15:45:54.502951Z")) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-resource-as-user"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-resource-as-user"), text = SharedTestDataADM.createResourceAsUser(SharedTestDataADM.anythingUser1) ) ) @@ -162,7 +157,7 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def updateResourceMetadataTestRequestsAndResponse: Future[Set[SourceCodeFileContent]] = { + private def updateResourceMetadataTestRequestsAndResponse: Future[Set[TestDataFileContent]] = { val resourceIri = "http://rdfh.ch/0001/a-thing" val newLabel = "test thing with modified label" val newPermissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:ProjectMember" @@ -170,8 +165,8 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-resource-metadata-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-resource-metadata-request"), text = SharedTestDataADM.updateResourceMetadata( resourceIri = resourceIri, lastModificationDate = None, @@ -180,8 +175,8 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) newModificationDate = newModificationDate ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-resource-metadata-request-with-last-mod-date"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-resource-metadata-request-with-last-mod-date"), text = SharedTestDataADM.updateResourceMetadata( resourceIri = resourceIri, lastModificationDate = Some(Instant.parse("2019-02-13T09:05:10Z")), @@ -190,8 +185,8 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) newModificationDate = newModificationDate ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-resource-metadata-response"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-resource-metadata-response"), text = SharedTestDataADM.successResponse("Resource metadata updated") ) ) @@ -343,18 +338,18 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Resources to return in test data. private val testResources: Map[String, IRI] = Map( "testding" -> SharedTestDataADM.TestDing.iri, - "page" -> "http://rdfh.ch/0803/7bbb8e59b703" + "thing-with-picture" -> "http://rdfh.ch/0001/a-thing-with-picture" ) - private def getResourceTestResponses: Future[Set[SourceCodeFileContent]] = { - val responseFutures: Iterable[Future[SourceCodeFileContent]] = testResources.map { + private def getResourceTestResponses: Future[Set[TestDataFileContent]] = { + val responseFutures: Iterable[Future[TestDataFileContent]] = testResources.map { case (filename, resourceIri) => val encodedResourceIri = URLEncoder.encode(resourceIri, "UTF-8") for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ResourcesBasePathString/$encodedResourceIri")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(filename), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(filename), text = responseStr ) } @@ -391,11 +386,11 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def getResourcesPreviewTestResponse: Future[SourceCodeFileContent] = { + private def getResourcesPreviewTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl/v2/resourcespreview/${SharedTestDataADM.AThing.iriEncoded}")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("resource-preview"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("resource-preview"), text = responseStr ) } @@ -489,11 +484,11 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def getResourceGraphTestResponse: Future[SourceCodeFileContent] = { + private def getResourceGraphTestResponse: Future[TestDataFileContent] = { for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl/v2/graph/${URLEncoder.encode("http://rdfh.ch/0001/start", "UTF-8")}?direction=both")) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("resource-graph"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("resource-graph"), text = responseStr ) @@ -532,21 +527,21 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def deleteResourceTestRequestAndResponse: Future[Set[SourceCodeFileContent]] = { + private def deleteResourceTestRequestAndResponse: Future[Set[TestDataFileContent]] = { val resourceIri = "http://rdfh.ch/0001/a-thing" val lastModificationDate = Instant.parse("2019-12-12T10:23:25.836924Z") FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("delete-resource-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("delete-resource-request"), text = SharedTestDataADM.deleteResource( resourceIri = resourceIri, lastModificationDate = lastModificationDate ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("delete-resource-response"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("delete-resource-response"), text = SharedTestDataADM.successResponse("Resource marked as deleted")) ) ) @@ -585,13 +580,13 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - private def eraseResourceTestRequest: Future[SourceCodeFileContent] = { + private def eraseResourceTestRequest: Future[TestDataFileContent] = { val resourceIri = "http://rdfh.ch/0001/thing-with-history" val resourceLastModificationDate = Instant.parse("2019-02-13T09:05:10Z") FastFuture.successful( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("erase-resource-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("erase-resource-request"), text = SharedTestDataADM.eraseResource( resourceIri = resourceIri, lastModificationDate = resourceLastModificationDate @@ -602,7 +597,7 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { for { getResponses <- getResourceTestResponses createRequests <- createResourceTestRequests diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala index 3fd81a32e2..6090def81a 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala @@ -31,8 +31,7 @@ import org.knora.webapi.responders.v2.search.SparqlQueryConstants import org.knora.webapi.responders.v2.search.gravsearch.GravsearchParser import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV2} import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.clientapi.{ClientEndpoint, ClientFunction, SourceCodeFileContent, SourceCodeFilePath} -import org.knora.webapi.util.{SmartIri, StringFormatter} +import org.knora.webapi.util.{ClientEndpoint, SmartIri, StringFormatter, TestDataFileContent, TestDataFilePath} import scala.concurrent.{ExecutionContext, Future} @@ -41,12 +40,8 @@ import scala.concurrent.{ExecutionContext, Future} */ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator with ClientEndpoint { - // Definitions for ClientEndpoint - override val name: String = "SearchEndpoint" + // Directory name for generated test data override val directoryName: String = "search" - override val urlPath: String = "search" - override val description: String = "An endpoint for searching data in Knora." - override val functions: Seq[ClientFunction] = Seq.empty private val LIMIT_TO_PROJECT = "limitToProject" private val LIMIT_TO_RESOURCE_CLASS = "limitToResourceClass" @@ -351,16 +346,16 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit // Search results to return in test data. private val testSearches: Map[String, IRI] = Map( "things" -> SharedTestDataADM.gravsearchComplexThingSmallerThanDecimal, - "regions" -> SharedTestDataADM.gravsearchComplexRegionsForPage + "thing-links" -> SharedTestDataADM.gravsearchThingLinks ) - private def getSearchTestResponses: Future[Set[SourceCodeFileContent]] = { - val responseFutures: Iterable[Future[SourceCodeFileContent]] = testSearches.map { + private def getSearchTestResponses: Future[Set[TestDataFileContent]] = { + val responseFutures: Iterable[Future[TestDataFileContent]] = testSearches.map { case (filename, search) => for { responseStr <- doTestDataRequest(Post(s"$baseApiUrl/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, search))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(filename), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(filename), text = responseStr ) } @@ -451,7 +446,7 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { getSearchTestResponses } } \ No newline at end of file diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/V2ClientApi.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/V2ClientApi.scala index 4c96394984..8de0e646dd 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/V2ClientApi.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/V2ClientApi.scala @@ -20,27 +20,12 @@ package org.knora.webapi.routing.v2 import org.knora.webapi.routing.KnoraRouteData -import org.knora.webapi.util.clientapi.{ApiSerialisationFormat, ClientApi, ClientEndpoint, JsonLD} -import org.knora.webapi.util.{SmartIri, StringFormatter} +import org.knora.webapi.util.{ClientApi, ClientEndpoint} +/** + * Represents the structure of client test data generated for Knora API v2. + */ class V2ClientApi(routeData: KnoraRouteData) extends ClientApi { - implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - /** - * The serialisation format used by this [[ClientApi]]. - */ - override val serialisationFormat: ApiSerialisationFormat = JsonLD - - /** - * Don't generate endpoints for this API. - */ - override val generateEndpoints: Boolean = false - - /** - * Don't generate classes for this API. - */ - override val generateClasses: Boolean = false - /** * The endpoints in this [[ClientApi]]. */ @@ -53,61 +38,7 @@ class V2ClientApi(routeData: KnoraRouteData) extends ClientApi { ) /** - * The name of this [[ClientApi]]. - */ - override val name: String = "V2Endpoint" - - /** - * The directory name to be used for this API's code. + * The directory name to be used for the generated test data. */ override val directoryName: String = "v2" - - /** - * The URL path of this [[ClientApi]]. - */ - override val urlPath: String = "/v2" - - /** - * A description of this [[ClientApi]]. - */ - override val description: String = "The Knora API v2." - - /** - * A map of class IRIs to their read-only properties. - */ - override val classesWithReadOnlyProperties: Map[SmartIri, Set[SmartIri]] = Map.empty - - /** - * A set of IRIs of classes that represent API requests and that therefore do not need `Stored*` - * subclasses. - */ - override val requestClasses: Set[SmartIri] = Set.empty - - /** - * A set of IRIs of classes that are always read-only and that therefore do not need `Stored*` - * or `Read*` subclasses. - */ - override val readOnlyClasses: Set[SmartIri] = Set.empty - - /** - * A set of IRIs of classes that represent API responses. - */ - override val responseClasses: Set[SmartIri] = Set.empty - - /** - * A set of property IRIs that are used for the unique IDs of objects. - */ - override val idProperties: Set[SmartIri] = Set.empty - - /** - * A map of class IRIs to maps of property IRIs to non-standard names that those properties must have - * in those classes. Needed only for JSON, and only if two different properties should have the same name in - * different classes. `JsonInstanceInspector` also needs to know about these. - */ - override val propertyNames: Map[SmartIri, Map[SmartIri, String]] = Map.empty - /** - * A map of class IRIs to IRIs of optional set properties. Such properties have cardinality 0-n, and should - * be made optional in generated code. - */ - override val classesWithOptionalSetProperties: Map[SmartIri, Set[SmartIri]] = Map.empty } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala index 149cf4391c..cf966281a9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala @@ -34,8 +34,7 @@ import org.knora.webapi.messages.v2.responder.resourcemessages.ResourcesGetReque import org.knora.webapi.messages.v2.responder.valuemessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV2} import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.SmartIri -import org.knora.webapi.util.clientapi.{ClientEndpoint, ClientFunction, SourceCodeFileContent, SourceCodeFilePath} +import org.knora.webapi.util.{ClientEndpoint, SmartIri, TestDataFileContent, TestDataFilePath} import org.knora.webapi.util.jsonld.{JsonLDDocument, JsonLDUtil} import scala.concurrent.{ExecutionContext, Future} @@ -52,12 +51,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit import ValuesRouteV2._ - // Definitions for ClientEndpoint - override val name: String = "ValuesEndpoint" + // Directory name for generated test data override val directoryName: String = "values" - override val urlPath: String = "values" - override val description: String = "An endpoint for working with Knora values." - override val functions: Seq[ClientFunction] = Seq.empty /** * Returns the route. @@ -138,24 +133,24 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Provides JSON-LD responses to requests for values, for use in tests of generated client code. */ - private def getValueTestResponses: Future[Set[SourceCodeFileContent]] = { - val responseFutures: Iterable[Future[SourceCodeFileContent]] = testDingValues.map { + private def getValueTestResponses: Future[Set[TestDataFileContent]] = { + val responseFutures: Iterable[Future[TestDataFileContent]] = testDingValues.map { case (valueTypeName, valueUuid) => for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ValuesBasePathString/${SharedTestDataADM.TestDing.iriEncoded}/$valueUuid") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingUser1.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(s"get-$valueTypeName-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(s"get-$valueTypeName-response"), text = responseStr ) } for { - files: Iterable[SourceCodeFileContent] <- Future.sequence(responseFutures) + files: Iterable[TestDataFileContent] <- Future.sequence(responseFutures) - getStillImageFileValueResponse: SourceCodeFileContent <- for { + getStillImageFileValueResponse: TestDataFileContent <- for { responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ValuesBasePathString/${SharedTestDataADM.AThingPicture.iriEncoded}/${SharedTestDataADM.AThingPicture.stillImageFileValueUuid}") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingUser1.email, SharedTestDataADM.testPass))) - } yield SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath(s"get-still-image-file-value-response"), + } yield TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath(s"get-still-image-file-value-response"), text = responseStr ) } yield files.toSet + getStillImageFileValueResponse @@ -203,56 +198,56 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns JSON-LD requests for creating values in tests of generated client code. */ - private def createValueTestRequests: Future[Set[SourceCodeFileContent]] = { + private def createValueTestRequests: Future[Set[TestDataFileContent]] = { FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-int-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-int-value-request"), text = SharedTestDataADM.createIntValueRequest( resourceIri = SharedTestDataADM.AThing.iri, intValue = 4 ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-int-value-with-custom-permissions-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-int-value-with-custom-permissions-request"), text = SharedTestDataADM.createIntValueWithCustomPermissionsRequest( resourceIri = SharedTestDataADM.AThing.iri, intValue = 4, customPermissions = "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-text-value-without-standoff-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-text-value-without-standoff-request"), text = SharedTestDataADM.createTextValueWithoutStandoffRequest( resourceIri = SharedTestDataADM.AThing.iri, valueAsString = "How long is a piece of string?" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-text-value-with-standoff-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-text-value-with-standoff-request"), text = SharedTestDataADM.createTextValueWithStandoffRequest( resourceIri = SharedTestDataADM.AThing.iri, textValueAsXml = SharedTestDataADM.textValue1AsXmlWithStandardMapping, mappingIri = SharedTestDataADM.standardMappingIri ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-text-value-with-comment-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-text-value-with-comment-request"), text = SharedTestDataADM.createTextValueWithCommentRequest( resourceIri = SharedTestDataADM.AThing.iri, valueAsString = "This is the text.", valueHasComment = "This is the comment on the text." ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-decimal-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-decimal-value-request"), text = SharedTestDataADM.createDecimalValueRequest( resourceIri = SharedTestDataADM.AThing.iri, decimalValue = BigDecimal(4.3) ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-date-value-with-day-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-date-value-with-day-precision-request"), text = SharedTestDataADM.createDateValueWithDayPrecisionRequest( resourceIri = SharedTestDataADM.AThing.iri, dateValueHasCalendar = "GREGORIAN", @@ -266,8 +261,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-date-value-with-month-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-date-value-with-month-precision-request"), text = SharedTestDataADM.createDateValueWithMonthPrecisionRequest( resourceIri = SharedTestDataADM.AThing.iri, dateValueHasCalendar = "GREGORIAN", @@ -279,8 +274,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-date-value-with-year-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-date-value-with-year-precision-request"), text = SharedTestDataADM.createDateValueWithYearPrecisionRequest( resourceIri = SharedTestDataADM.AThing.iri, dateValueHasCalendar = "GREGORIAN", @@ -290,65 +285,65 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-boolean-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-boolean-value-request"), text = SharedTestDataADM.createBooleanValueRequest( resourceIri = SharedTestDataADM.AThing.iri, booleanValue = true ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-geometry-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-geometry-value-request"), text = SharedTestDataADM.createGeometryValueRequest( resourceIri = SharedTestDataADM.AThing.iri, geometryValue = SharedTestDataADM.geometryValue1 ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-interval-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-interval-value-request"), text = SharedTestDataADM.createIntervalValueRequest( resourceIri = SharedTestDataADM.AThing.iri, intervalStart = BigDecimal("1.2"), intervalEnd = BigDecimal("3.4") ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-time-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-time-value-request"), text = SharedTestDataADM.createTimeValueRequest( resourceIri = SharedTestDataADM.AThing.iri, timeStamp = Instant.parse("2019-08-28T15:59:12.725007Z") ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-list-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-list-value-request"), text = SharedTestDataADM.createListValueRequest( resourceIri = SharedTestDataADM.AThing.iri, listNode = "http://rdfh.ch/lists/0001/treeList03" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-color-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-color-value-request"), text = SharedTestDataADM.createColorValueRequest( resourceIri = SharedTestDataADM.AThing.iri, color = "#ff3333" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-uri-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-uri-value-request"), text = SharedTestDataADM.createUriValueRequest( resourceIri = SharedTestDataADM.AThing.iri, uri = "https://www.knora.org" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-geoname-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-geoname-value-request"), text = SharedTestDataADM.createGeonameValueRequest( resourceIri = SharedTestDataADM.AThing.iri, geonameCode = "2661604" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-link-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-link-value-request"), text = SharedTestDataADM.createLinkValueRequest( resourceIri = SharedTestDataADM.AThing.iri, targetResourceIri = "http://rdfh.ch/0001/A67ka6UQRHWf313tbhQBjw" @@ -358,7 +353,7 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } - private def createValueTestResponse: Future[SourceCodeFileContent] = { + private def createValueTestResponse: Future[TestDataFileContent] = { val createValueResponseV2: CreateValueResponseV2 = CreateValueResponseV2( valueIri = SharedTestDataADM.testResponseValueIri, valueType = OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri, @@ -367,8 +362,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) Future { - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("create-value-response"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("create-value-response"), text = createValueResponseV2.toJsonLDDocument( targetSchema = ApiV2Complex, settings = settings, @@ -414,19 +409,19 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns JSON-LD requests for updating values in tests of generated client code. */ - private def updateValueTestRequests: Future[Set[SourceCodeFileContent]] = { + private def updateValueTestRequests: Future[Set[TestDataFileContent]] = { FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-int-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-int-value-request"), text = SharedTestDataADM.updateIntValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.intValueIri, intValue = 5 ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-int-value-with-custom-permissions-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-int-value-with-custom-permissions-request"), text = SharedTestDataADM.updateIntValueWithCustomPermissionsRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.intValueIri, @@ -434,24 +429,24 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit customPermissions = "CR http://rdfh.ch/groups/0001/thing-searcher" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-int-value-permissions-only-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-int-value-permissions-only-request"), text = SharedTestDataADM.updateIntValuePermissionsOnlyRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.intValueIri, customPermissions = "CR http://rdfh.ch/groups/0001/thing-searcher|V knora-admin:KnownUser" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-text-value-without-standoff-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-text-value-without-standoff-request"), text = SharedTestDataADM.updateTextValueWithoutStandoffRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.textValueWithoutStandoffIri, valueAsString = "This is the updated text." ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-text-value-with-standoff-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-text-value-with-standoff-request"), text = SharedTestDataADM.updateTextValueWithStandoffRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.textValueWithStandoffIri, @@ -459,8 +454,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit mappingIri = SharedTestDataADM.standardMappingIri ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-text-value-with-comment-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-text-value-with-comment-request"), text = SharedTestDataADM.updateTextValueWithCommentRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.textValueWithoutStandoffIri, @@ -468,16 +463,16 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit valueHasComment = "this is an updated comment" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-decimal-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-decimal-value-request"), text = SharedTestDataADM.updateDecimalValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.decimalValueIri, decimalValue = BigDecimal(5.6) ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-date-value-with-day-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-date-value-with-day-precision-request"), text = SharedTestDataADM.updateDateValueWithDayPrecisionRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.dateValueIri, @@ -492,8 +487,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-date-value-with-month-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-date-value-with-month-precision-request"), text = SharedTestDataADM.updateDateValueWithMonthPrecisionRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.dateValueIri, @@ -506,8 +501,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-date-value-with-year-precision-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-date-value-with-year-precision-request"), text = SharedTestDataADM.updateDateValueWithYearPrecisionRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.dateValueIri, @@ -518,24 +513,24 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit dateValueHasEndEra = "CE" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-boolean-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-boolean-value-request"), text = SharedTestDataADM.updateBooleanValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.booleanValueIri, booleanValue = false ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-geometry-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-geometry-value-request"), text = SharedTestDataADM.updateGeometryValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.geomValueIri, geometryValue = SharedTestDataADM.geometryValue2 ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-interval-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-interval-value-request"), text = SharedTestDataADM.updateIntervalValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.intervalValueIri, @@ -543,56 +538,56 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit intervalEnd = BigDecimal("7.8") ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-time-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-time-value-request"), text = SharedTestDataADM.updateTimeValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.timeValueIri, timeStamp = Instant.parse("2019-12-16T09:33:22.082549Z") ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-list-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-list-value-request"), text = SharedTestDataADM.updateListValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.listValueIri, listNode = "http://rdfh.ch/lists/0001/treeList02" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-color-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-color-value-request"), text = SharedTestDataADM.updateColorValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.colorValueIri, color = "#ff3344" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-uri-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-uri-value-request"), text = SharedTestDataADM.updateUriValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.uriValueIri, uri = "https://docs.knora.org" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-geoname-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-geoname-value-request"), text = SharedTestDataADM.updateGeonameValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.geonameValueIri, geonameCode = "2988507" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-link-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-link-value-request"), text = SharedTestDataADM.updateLinkValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.linkValueIri, targetResourceIri = "http://rdfh.ch/0001/5IEswyQFQp2bxXDrOyEfEA" ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-still-image-file-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-still-image-file-value-request"), text = SharedTestDataADM.updateStillImageFileValueRequest( resourceIri = "http://rdfh.ch/0001/a-thing-picture", valueIri = "http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q", @@ -603,7 +598,7 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } - private def updateValueTestResponse: Future[SourceCodeFileContent] = { + private def updateValueTestResponse: Future[TestDataFileContent] = { val createValueResponseV2: UpdateValueResponseV2 = UpdateValueResponseV2( valueIri = SharedTestDataADM.testResponseValueIri, valueType = OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri, @@ -612,8 +607,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) Future { - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("update-value-response"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("update-value-response"), text = createValueResponseV2.toJsonLDDocument( targetSchema = ApiV2Complex, settings = settings, @@ -659,19 +654,19 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns JSON-LD requests for deleting values in tests of generated client code. */ - private def deleteValueTestRequests: Future[Set[SourceCodeFileContent]] = { + private def deleteValueTestRequests: Future[Set[TestDataFileContent]] = { FastFuture.successful( Set( - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("delete-int-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("delete-int-value-request"), text = SharedTestDataADM.deleteIntValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.intValueIri, maybeDeleteComment = Some("this value was incorrect") ) ), - SourceCodeFileContent( - filePath = SourceCodeFilePath.makeJsonPath("delete-link-value-request"), + TestDataFileContent( + filePath = TestDataFilePath.makeJsonPath("delete-link-value-request"), text = SharedTestDataADM.deleteLinkValueRequest( resourceIri = SharedTestDataADM.TestDing.iri, valueIri = SharedTestDataADM.TestDing.linkValueIri @@ -681,14 +676,14 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } - override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { + override def getTestData(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { for { - getResponses: Set[SourceCodeFileContent] <- getValueTestResponses - createRequests: Set[SourceCodeFileContent] <- createValueTestRequests - updateRequests: Set[SourceCodeFileContent] <- updateValueTestRequests - deleteRequests: Set[SourceCodeFileContent] <- deleteValueTestRequests - createValueResponse: SourceCodeFileContent <- createValueTestResponse - updateValueResponse: SourceCodeFileContent <- updateValueTestResponse + getResponses: Set[TestDataFileContent] <- getValueTestResponses + createRequests: Set[TestDataFileContent] <- createValueTestRequests + updateRequests: Set[TestDataFileContent] <- updateValueTestRequests + deleteRequests: Set[TestDataFileContent] <- deleteValueTestRequests + createValueResponse: TestDataFileContent <- createValueTestResponse + updateValueResponse: TestDataFileContent <- updateValueTestResponse } yield getResponses ++ createRequests ++ updateRequests ++ deleteRequests + createValueResponse + updateValueResponse } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/ClientApi.scala b/webapi/src/main/scala/org/knora/webapi/util/ClientApi.scala new file mode 100644 index 0000000000..fe9766ae2e --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/util/ClientApi.scala @@ -0,0 +1,148 @@ +/* + * Copyright © 2015-2019 the contributors (see Contributors.md). + * + * This file is part of Knora. + * + * Knora is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Knora is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with Knora. If not, see . + */ + +package org.knora.webapi.util + +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.HttpRequest +import akka.stream.ActorMaterializer +import org.knora.webapi.ClientApiGenerationException + +import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} + +/** + * Represents a client API for the purposes of generating client test data. + */ +trait ClientApi { + /** + * The name of a directory in which the client test data can be generated. + */ + val directoryName: String + + /** + * The endpoints available in the API. + */ + val endpoints: Seq[ClientEndpoint] + + /** + * Returns test data for this API and its endpoints. + * + * @param testDataDirectoryPath the path of the top-level test data directory. + * @return a set of test data files to be used for testing this API and its endpoints. + */ + def getTestData(testDataDirectoryPath: Seq[String])(implicit executionContext: ExecutionContext, + actorSystem: ActorSystem, + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] = { + for { + endpointTestData <- Future.sequence { + endpoints.map { + endpoint: ClientEndpoint => + for { + endpointTestData: Set[TestDataFileContent] <- endpoint.getTestData + } yield endpointTestData.map { + sourceCodeFileContent: TestDataFileContent => + sourceCodeFileContent.copy( + filePath = sourceCodeFileContent.filePath.copy( + directoryPath = testDataDirectoryPath :+ directoryName :+ endpoint.directoryName + ) + ) + } + } + } + } yield endpointTestData.flatten.toSet + } +} + +/** + * Represents a client endpoint. + */ +trait ClientEndpoint { + /** + * The name of a directory in which the endpoint test data can be generated. + */ + val directoryName: String + + /** + * Makes a request to Knora to get test data. + * + * @param request the request to send. + * @return the string value of the response. + */ + protected def doTestDataRequest(request: HttpRequest)(implicit executionContext: ExecutionContext, + actorSystem: ActorSystem, + materializer: ActorMaterializer): Future[String] = { + for { + response <- Http().singleRequest(request) + responseStr <- response.entity.toStrict(10240.millis).map(_.data.decodeString("UTF-8")) + + _ = if (response.status.isFailure) { + throw ClientApiGenerationException(s"Failed to get test data: $responseStr") + } + } yield responseStr + } + + /** + * Returns test data for this endpoint. + * + * @return a set of test data files to be used for testing this endpoint. The directory paths should be empty. + */ + def getTestData(implicit executionContext: ExecutionContext, + actorSystem: ActorSystem, + materializer: ActorMaterializer): Future[Set[TestDataFileContent]] +} + +/** + * Represents the filesystem path of a file containing generated test data. + * + * @param directoryPath the path of the directory containing the file, + * relative to the root directory of the source tree. + * @param filename the filename, without the file extension. + * @param fileExtension the file extension. + */ +case class TestDataFilePath(directoryPath: Seq[String], filename: String, fileExtension: String) { + override def toString: String = { + (directoryPath :+ filename + "." + fileExtension).mkString("/") + } +} + +object TestDataFilePath { + /** + * A convenience method that makes a path for a JSON file in the current directory. + * + * @param filename the filename. + * @return the file path. + */ + def makeJsonPath(filename: String): TestDataFilePath = { + TestDataFilePath( + directoryPath = Seq.empty, + filename = filename, + fileExtension = "json" + ) + } +} + +/** + * Represents a file containing generated client API test data. + * + * @param filePath the file path in which the test data should be saved. + * @param text the source code. + */ +case class TestDataFileContent(filePath: TestDataFilePath, text: String) diff --git a/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala index 7b49dad8c6..c86cac85d1 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala @@ -19,7 +19,6 @@ package org.knora.webapi.util -import java.net.URLDecoder import java.nio.ByteBuffer import java.text.ParseException import java.time._ @@ -44,7 +43,6 @@ import org.knora.webapi.messages.v2.responder.KnoraContentV2 import org.knora.webapi.messages.v2.responder.standoffmessages._ import org.knora.webapi.util.IriConversions._ import org.knora.webapi.util.JavaUtil.Optional -import org.knora.webapi.util.clientapi.{ClientCollectionTypeParser, CollectionType} import spray.json._ import scala.concurrent.{ExecutionContext, Future} @@ -359,22 +357,20 @@ object StringFormatter { /** * Holds information extracted from the IRI. * - * @param iriType the type of the IRI. - * @param projectCode the IRI's project code, if any. - * @param ontologyName the IRI's ontology name, if any. - * @param entityName the IRI's entity name, if any. - * @param clientCollectionType the [[CollectionType]] represented by this IRI, if any. - * @param resourceID if this is a resource IRI or value IRI, its resource ID. - * @param valueID if this is a value IRI, its value ID. - * @param standoffStartIndex if this is a standoff IRI, its start index. - * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. - * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. + * @param iriType the type of the IRI. + * @param projectCode the IRI's project code, if any. + * @param ontologyName the IRI's ontology name, if any. + * @param entityName the IRI's entity name, if any. + * @param resourceID if this is a resource IRI or value IRI, its resource ID. + * @param valueID if this is a value IRI, its value ID. + * @param standoffStartIndex if this is a standoff IRI, its start index. + * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. + * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. */ private case class SmartIriInfo(iriType: IriType, projectCode: Option[String] = None, ontologyName: Option[String] = None, entityName: Option[String] = None, - clientCollectionType: Option[CollectionType] = None, resourceID: Option[String] = None, valueID: Option[String] = None, standoffStartIndex: Option[Int] = None, @@ -509,11 +505,6 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { */ def isKnoraApiV2EntityIri: Boolean - /** - * Returns `true` if this IRI represents a collection type for use in client code generation. - */ - def isClientCollectionTypeIri: Boolean - /** * Returns the IRI's project code, if any. */ @@ -549,11 +540,6 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { */ def getEntityName: String - /** - * If this IRI represents a collection type for use in client code generation, returns the collection type. - */ - def getClientCollectionType: CollectionType - /** * If this is a Knora ontology IRI, constructs a Knora entity IRI based on it. Otherwise, throws [[DataConversionException]]. * @@ -1003,22 +989,14 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma val hashPos = iri.lastIndexOf('#') - val (namespace: String, entityName: Option[String], maybeClientCollectionType: Option[CollectionType]) = if (hashPos >= 0 && hashPos < iri.length) { + val (namespace: String, entityName: Option[String]) = if (hashPos >= 0 && hashPos < iri.length) { val namespace = iri.substring(0, hashPos) val entityName = iri.substring(hashPos + 1) - // Is the entity a type annotation representing a collection type for client code generation? - if (entityName.startsWith(ClientCollectionTypeKeyword)) { - // Yes. Parse it. - val typeStr = URLDecoder.decode(entityName, "UTF-8").substring(ClientCollectionTypeKeyword.length) - val clientCollectionType = ClientCollectionTypeParser.parse(typeStr = typeStr, ontologyIri = toSmartIri(namespace)) - (namespace, Some(entityName), Some(clientCollectionType)) - } else { - // No. Validate the entity name as an NCName. - (namespace, Some(validateNCName(entityName, errorFun)), None) - } + // Validate the entity name as an NCName. + (namespace, Some(validateNCName(entityName, errorFun))) } else { - (iri, None, None) + (iri, None) } // Remove the URL scheme (http://), and split the remainder of the namespace into slash-delimited segments. @@ -1127,7 +1105,6 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma projectCode = projectCode, ontologyName = Some(ontologyName), entityName = entityName, - clientCollectionType = maybeClientCollectionType, ontologySchema = ontologySchema, isBuiltInDef = hasBuiltInOntologyName, sharedOntology = sharedOntology @@ -1165,9 +1142,7 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma override def isKnoraOntologyIri: Boolean = iriInfo.iriType == KnoraDefinitionIri && iriInfo.ontologyName.nonEmpty && iriInfo.entityName.isEmpty - override def isKnoraEntityIri: Boolean = iriInfo.iriType == KnoraDefinitionIri && iriInfo.entityName.nonEmpty && iriInfo.clientCollectionType.isEmpty - - override def isClientCollectionTypeIri: Boolean = iriInfo.clientCollectionType.nonEmpty + override def isKnoraEntityIri: Boolean = iriInfo.iriType == KnoraDefinitionIri && iriInfo.entityName.nonEmpty override def getProjectCode: Option[String] = iriInfo.projectCode @@ -1224,13 +1199,6 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } } - override def getClientCollectionType: CollectionType = { - iriInfo.clientCollectionType match { - case Some(collectionType) => collectionType - case None => throw DataConversionException(s"Expected a client collection type IRI: $iri") - } - } - override def getOntologySchema: Option[OntologySchema] = iriInfo.ontologySchema override def checkApiV2Schema(allowedSchema: ApiV2Schema, errorFun: => Nothing): SmartIri = { diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala deleted file mode 100644 index afbbcadfb8..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala +++ /dev/null @@ -1,971 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpRequest -import akka.stream.ActorMaterializer -import org.knora.webapi.ClientApiGenerationException -import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.Cardinality -import org.knora.webapi.util.SmartIri - -import scala.concurrent.duration._ -import scala.concurrent.{ExecutionContext, Future} - -/** - * A trait for enumerated values representing API serialisation formats. - */ -trait ApiSerialisationFormat - -/** - * Indicates that an API uses plain JSON as its serialisation format. - */ -case object Json extends ApiSerialisationFormat - -/** - * Indicates that an API uses JSON-LD as its serialisation format. - */ -case object JsonLD extends ApiSerialisationFormat - -/** - * Represents a client API. - */ -trait ClientApi { - /** - * If `true`, generate endpoints for this API. - */ - val generateEndpoints: Boolean = true - - /** - * If `true`, generate classes for this API. - */ - val generateClasses: Boolean = true - - /** - * The serialisation format used by the API. - */ - val serialisationFormat: ApiSerialisationFormat - - /** - * The machine-readable name of the API. - */ - val name: String - - /** - * The name of a directory in which the API code can be generated. - */ - val directoryName: String - - /** - * The URL path of the API. - */ - val urlPath: String - - /** - * A human-readable description of the API. - */ - val description: String - - /** - * The endpoints available in the API. - */ - val endpoints: Seq[ClientEndpoint] - - /** - * A map of class IRIs to their read-only properties. Each class in this collection will - * be separated into a base class without the read-only properties, and a subclass with the - * read-only properties. - */ - val classesWithReadOnlyProperties: Map[SmartIri, Set[SmartIri]] - - /** - * A map of class IRIs to IRIs of optional set properties. Such properties have cardinality 0-n, and should - * be made optional in generated code. - */ - val classesWithOptionalSetProperties: Map[SmartIri, Set[SmartIri]] - - /** - * A set of IRIs of classes that represent API requests and that therefore do not need `Stored*` - * subclasses. - */ - val requestClasses: Set[SmartIri] - - /** - * A set of IRIs of classes that are always read-only and that therefore do not need `Stored*` - * or `Read*` subclasses. - */ - val readOnlyClasses: Set[SmartIri] - - /** - * A set of IRIs of classes that represent API responses and whose contents are therefore - * always read-only. - */ - val responseClasses: Set[SmartIri] - - /** - * A set of property IRIs that are used for the unique IDs of objects. - */ - val idProperties: Set[SmartIri] - - /** - * A map of class IRIs to maps of property IRIs to non-standard names that those properties must have - * in those classes. Needed only for JSON, and only if two different properties should have the same name in - * different classes. `JsonInstanceInspector` also needs to know about these. - */ - val propertyNames: Map[SmartIri, Map[SmartIri, String]] - - /** - * Class IRIs that are used by this API, other than the ones used in endpoints. - */ - val generalClassIrisUsed: Set[SmartIri] = Set.empty - - /** - * The IRIs of the classes used by this API. - */ - lazy val classIrisUsed: Set[SmartIri] = endpoints.flatMap(_.classIrisUsed).toSet ++ generalClassIrisUsed - - /** - * Returns test data for this API and its endpoints. - * - * @param testDataDirectoryPath the path of the top-level test data directory. - * @return a set of test data files to be used for testing this API and its endpoints. - */ - def getTestData(testDataDirectoryPath: Seq[String])(implicit executionContext: ExecutionContext, - actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] = { - for { - endpointTestData <- Future.sequence { - endpoints.map { - endpoint: ClientEndpoint => - for { - endpointTestData: Set[SourceCodeFileContent] <- endpoint.getTestData - } yield endpointTestData.map { - sourceCodeFileContent: SourceCodeFileContent => - sourceCodeFileContent.copy( - filePath = sourceCodeFileContent.filePath.copy( - directoryPath = testDataDirectoryPath :+ directoryName :+ endpoint.directoryName - ) - ) - } - } - } - } yield endpointTestData.flatten.toSet - } -} - -/** - * Represents a client endpoint. - */ -trait ClientEndpoint { - /** - * The machine-readable name of the endpoint. - */ - val name: String - - /** - * The name of a directory in which the endpoint code can be generated. - */ - val directoryName: String - - /** - * The URL path of the endpoint, relative to its API path. - */ - val urlPath: String - - /** - * A human-readable description of the endpoint. - */ - val description: String - - /** - * The functions provided by the endpoint. - */ - val functions: Seq[ClientFunction] - - /** - * The IRIs of the classes used by this endpoint. - */ - lazy val classIrisUsed: Set[SmartIri] = functions.flatMap { - function => - val maybeReturnedClass: Option[SmartIri] = function.returnType match { - case classRef: ClassRef => Some(classRef.classIri) - case _ => None - } - - val paramClasses: Set[SmartIri] = function.params.map { - param => param.objectType - }.collect { - case classRef: ClassRef => classRef.classIri - }.toSet - - paramClasses ++ maybeReturnedClass - }.toSet - - /** - * Makes a request to Knora to get test data. - * - * @param request the request to send. - * @return the string value of the response. - */ - protected def doTestDataRequest(request: HttpRequest)(implicit executionContext: ExecutionContext, - actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[String] = { - for { - response <- Http().singleRequest(request) - responseStr <- response.entity.toStrict(10240.millis).map(_.data.decodeString("UTF-8")) - - _ = if (response.status.isFailure) { - throw ClientApiGenerationException(s"Failed to get test data: $responseStr") - } - } yield responseStr - } - - /** - * Returns test data for this endpoint. - * - * @return a set of test data files to be used for testing this endpoint. The directory paths should be empty. - */ - def getTestData(implicit executionContext: ExecutionContext, - actorSystem: ActorSystem, - materializer: ActorMaterializer): Future[Set[SourceCodeFileContent]] -} - -/** - * A DSL for defining functions in client endpoints. - */ -object EndpointFunctionDSL { - - import scala.language.implicitConversions - - /** - * Constructs a [[ClientHttpRequest]] for a `GET` request. - * - * @param path the URL path to be used in the request. - * @param params the query parameters to be used in the request. - * @return a [[ClientHttpRequest]]. - */ - def httpGet(path: Seq[UrlComponent], params: Seq[(String, Value)] = Seq.empty): ClientHttpRequest = - http(httpMethod = GET, path = path, params = params, body = None) - - /** - * Constructs a [[ClientHttpRequest]] for a `POST` request. - * - * @param path the URL path to be used in the request. - * @param params the query parameters to be used in the request. - * @param body the body of the request. - * @return a [[ClientHttpRequest]]. - */ - def httpPost(path: Seq[UrlComponent], params: Seq[(String, Value)] = Seq.empty, body: Option[HttpRequestBody] = None): ClientHttpRequest = - http(httpMethod = POST, path = path, params = params, body = body) - - /** - * Constructs a [[ClientHttpRequest]] for a `PUT` request. - * - * @param path the URL path to be used in the request. - * @param params the query parameters to be used in the request. - * @param body the body of the request. - * @return a [[ClientHttpRequest]]. - */ - def httpPut(path: Seq[UrlComponent], params: Seq[(String, Value)] = Seq.empty, body: Option[HttpRequestBody] = None): ClientHttpRequest = - http(httpMethod = PUT, path = path, params = params, body = body) - - /** - * Constructs a [[ClientHttpRequest]] for a `DELETE` request. - * - * @param path the URL path to be used in the request. - * @param params the query parameters to be used in the request. - * @return a [[ClientHttpRequest]]. - */ - def httpDelete(path: Seq[UrlComponent], params: Seq[(String, Value)] = Seq.empty): ClientHttpRequest = - http(httpMethod = DELETE, path = path, params = params, body = None) - - /** - * Constructs a [[ClassRef]] for referring to a class in a function definition. - * - * @param classIri the IRI of the class. - * @return a [[ClassRef]] that can be used for referring to the class. - */ - def classRef(classIri: SmartIri): ClassRef = ClassRef(className = classIri.getEntityName.capitalize, classIri = classIri) - - /** - * Constructs a [[StringLiteralValue]]. - * - * @param value the value of the string literal. - * @return a [[StringLiteralValue]]. - */ - def str(value: String): StringLiteralValue = StringLiteralValue(value) - - /** - * A [[BooleanLiteralValue]] with the value `true`. - */ - val True: BooleanLiteralValue = BooleanLiteralValue(true) - - /** - * A [[BooleanLiteralValue]] with the value `false`. - */ - val False: BooleanLiteralValue = BooleanLiteralValue(false) - - /** - * Constructs an [[EnumDatatype]]. - * - * @param values the values of the enumeration. - * @return an [[EnumDatatype]]. - */ - def enum(values: String*): EnumDatatype = EnumDatatype(values.toSet) - - /** - * Constructs an [[ArgValue]] referring to a function argument. - * - * @param name the name of the argument. - * @return an [[ArgValue]]. - */ - def arg(name: String) = ArgValue(name) - - /** - * Constructs an [[ArgValue]] referring to a member of a function argument. - * - * @param name the name of the argument. - * @param member the name of the member of the argument. - * @return an [[ArgValue]]. - */ - def argMember(name: String, member: String) = ArgValue(name = name, memberVariableName = Some(member)) - - /** - * A URL path representing the base path of an endpoint. - */ - val BasePath: Seq[UrlComponent] = Seq.empty[UrlComponent] - - /** - * Constructs a [[JsonRequestBody]]. - * - * @param pairs the key-value pairs to be included in the JSON. - * @return a [[JsonRequestBody]]. - */ - def json(pairs: (String, Value)*): JsonRequestBody = JsonRequestBody(pairs) - - private def http(httpMethod: ClientHttpMethod, path: Seq[UrlComponent], params: Seq[(String, Value)], body: Option[HttpRequestBody] = None): ClientHttpRequest = { - // Collapse each run of strings into a single string. - val collapsedPath: Seq[UrlComponent] = if (path.isEmpty) { - path - } else { - (SlashUrlComponent +: path).foldLeft(Vector.empty[UrlComponent]) { - case (acc, component) => - acc.lastOption match { - case Some(last) => - (last, component) match { - case (lastStr: StringLiteralValue, thisStr: StringLiteralValue) => - acc.dropRight(1) :+ StringLiteralValue(lastStr.value + thisStr.value) - - case (SlashUrlComponent, thisStr: StringLiteralValue) => - acc.dropRight(1) :+ StringLiteralValue("/" + thisStr.value) - - case (lastStr: StringLiteralValue, SlashUrlComponent) => - acc.dropRight(1) :+ StringLiteralValue(lastStr.value + "/") - - case _ => acc :+ component - } - - case None => acc :+ component - } - } - } - - ClientHttpRequest(httpMethod = httpMethod, urlPath = collapsedPath, params = params, requestBody = body) - } - - implicit class Identifier(val name: String) extends AnyVal { - def description(desc: String): NameWithDescription = NameWithDescription(name = name, description = desc) - } - - implicit def urlComponentToSeq(urlComponent: UrlComponent): Seq[UrlComponent] = Seq(urlComponent) - - implicit class UrlComponentAsSeq(val urlComponent: UrlComponent) extends AnyVal { - def /(nextComponent: UrlComponent): Seq[UrlComponent] = Seq(urlComponent, SlashUrlComponent, nextComponent) - } - - implicit class UrlComponentSeq(val urlComponents: Seq[UrlComponent]) extends AnyVal { - def /(nextComponent: UrlComponent): Seq[UrlComponent] = urlComponents :+ SlashUrlComponent :+ nextComponent - } - - case class NameWithDescription(name: String, description: String) { - def params(paramList: FunctionParam*): NameWithDescriptionAndParams = NameWithDescriptionAndParams( - name = name, - description = description, - paramList = paramList - ) - - def paramType(objectType: ClientObjectType): FunctionParam = FunctionParam( - name = name, - objectType = objectType, - description = description - ) - } - - case class NameWithDescriptionAndParams(name: String, description: String, paramList: Seq[FunctionParam]) { - def doThis(implementation: FunctionImplementation): NameWithDescriptionParamsAndImplementation = NameWithDescriptionParamsAndImplementation( - name = name, - description = description, - paramList = paramList, - implementation = implementation - ) - } - - case class NameWithDescriptionParamsAndImplementation(name: String, description: String, paramList: Seq[FunctionParam], implementation: FunctionImplementation) { - def returns(clientObjectType: ClientObjectType): ClientFunction = ClientFunction( - name = name, - params = paramList, - returnType = clientObjectType, - implementation = implementation, - description = description - ) - } - -} - -/** - * Represents a client endpoint function. - * - * @param name the name of the function. - * @param params the parameters of the function. - * @param returnType the function's return type. - * @param implementation the implementation of the function. - * @param description a human-readable description of the function. - */ -case class ClientFunction(name: String, - params: Seq[FunctionParam], - returnType: ClientObjectType, - implementation: FunctionImplementation, - description: String) { - def withArgs(args: Value*): FunctionCall = FunctionCall(name = name, args = args) -} - -/** - * Represents a function parameter. - * - * @param name the name of the parameter. - * @param objectType the type of the parameter. - * @param description a human-readable description of the parameter. - */ -case class FunctionParam(name: String, - objectType: ClientObjectType, - description: String) - -/** - * Represents the implementation of a client endpoint function. - */ -trait FunctionImplementation - -/** - * Represents an HTTP request to be used as the implementation of a client function. - * - * @param httpMethod the HTTP method to be used. - * @param urlPath the URL path to be used. - * @param params the URL parameters to be included. - * @param requestBody if provided, the body of the HTTP request. - */ -case class ClientHttpRequest(httpMethod: ClientHttpMethod, - urlPath: Seq[UrlComponent], - params: Seq[(String, Value)], - requestBody: Option[HttpRequestBody] = None) extends FunctionImplementation { - /** - * Represents all URL elements, including query parameters, as a sequence of [[Value]] objects - * for concatenation. - */ - lazy val urlElementsAsValues: Seq[Value] = urlPath.map { - case SlashUrlComponent => StringLiteralValue("/") - case value: Value => value - } ++ paramsAsValues - - /** - * Represents the URL query parameters as a sequence of [[Value]] objects for concatenation. - */ - lazy val paramsAsValues: Seq[Value] = { - if (params.isEmpty) { - Seq.empty - } else { - val pairsAsValues = params.foldLeft(Seq.empty[Value]) { - case (acc: Seq[Value], (paramName: String, paramValue: Value)) => - val pairAsValues: Seq[Value] = Seq(StringLiteralValue(paramName), StringLiteralValue("="), paramValue) - - if (acc.isEmpty) { - pairAsValues - } else { - (acc :+ StringLiteralValue("&")) ++ pairAsValues - } - } - - (StringLiteralValue("?") +: pairsAsValues).foldLeft(Vector.empty[Value]) { - case (acc, value) => - acc.lastOption match { - case Some(last) => - (last, value) match { - case (lastStr: StringLiteralValue, thisStr: StringLiteralValue) => - acc.dropRight(1) :+ StringLiteralValue(lastStr.value + thisStr.value) - - case _ => acc :+ value - } - - case None => acc :+ value - } - } - } - } -} - -/** - * Represents a function call to be used as the implementation of a client endpoint function. - * - * @param name the name of the function to be called. - * @param args the arguments to be passed to the function call. - */ -case class FunctionCall(name: String, args: Seq[Value]) extends FunctionImplementation - -/** - * Indicates the HTTP method used in a client endpoint function. - */ -trait ClientHttpMethod - -/** - * Represents HTTP GET. - */ -case object GET extends ClientHttpMethod - -/** - * Represents HTTP POST. - */ -case object POST extends ClientHttpMethod - -/** - * Represents HTTP PUT. - */ -case object PUT extends ClientHttpMethod - -/** - * Represents HTTP DELETE. - */ -case object DELETE extends ClientHttpMethod - -/** - * Represents part of a URL path to be constructed for a client endpoint function. - */ -trait UrlComponent - -/** - * Represents a `/` character in a URL path. - */ -case object SlashUrlComponent extends UrlComponent - -/** - * Represents a value used in a function. - */ -trait Value extends UrlComponent - -/** - * Represents a literal value. - */ -trait LiteralValue extends Value - -/** - * Represents a string literal value. - * - * @param value the value of the string literal. - */ -case class StringLiteralValue(value: String) extends LiteralValue { - override def toString: String = value -} - -/** - * Represents a boolean literal value. - * - * @param value the value of the boolean literal. - */ -case class BooleanLiteralValue(value: Boolean) extends LiteralValue { - override def toString: String = value.toString -} - -/** - * Represents an integer literal value. - * - * @param value the value of the integer literal. - */ -case class IntegerLiteralValue(value: Int) extends LiteralValue { - override def toString: String = value.toString -} - -/** - * Represents a function argument. - * - * @param name the name of the parameter whose value is the argument. - * @param memberVariableName if provided, the name of a member variable of the argument, to be used instead of - * the argument itself. - * @param convertTo if provided, the type that the argument should be converted to. - */ -case class ArgValue(name: String, memberVariableName: Option[String] = None, convertTo: Option[ClientObjectType] = None) extends Value with HttpRequestBody { - def as(convertTo: ClientObjectType): ArgValue = copy(convertTo = Some(convertTo)) -} - -/** - * Represents the body of an HTTP request. - */ -trait HttpRequestBody - -/** - * Represents a JSON object to be sent as an HTTP request body. - * - * @param jsonObject the keys and values of the object. - */ -case class JsonRequestBody(jsonObject: Seq[(String, Value)]) extends HttpRequestBody - -/** - * A definition of a Knora API class, which can be used by a [[GeneratorBackEnd]] to generate client code. - * - * @param className the name of the class. - * @param classDescription a description of the class. - * @param classIri the IRI of the class in the Knora API. - * @param properties definitions of the properties used in the class. - * @param subClassOf the IRI of the class's base class (used only in generated subclasses). - */ -case class ClientClassDefinition(className: String, - classDescription: Option[String], - classIri: SmartIri, - properties: Vector[ClientPropertyDefinition], - subClassOf: Option[SmartIri] = None) { - /** - * The classes used by this class. - */ - lazy val classObjectTypesUsed: Set[ClassRef] = properties.foldLeft(Set.empty[ClassRef]) { - (acc, property) => - property.objectType match { - case typeWithClassIri: TypeWithClassIri => - typeWithClassIri.getClassRef match { - case Some(classRef) => - if (classRef.classIri == classIri) { - acc - } else { - acc + classRef - } - - case None => acc - } - - case _ => acc - } - } -} - -/** - * A definition of a Knora property as used in a particular class. - * - * @param propertyName the name of the property. - * @param propertyDescription a description of the property. - * @param propertyIri the IRI of the property in the Knora API. - * @param objectType the type of object that the property points to. - * @param cardinality the cardinality of the property in the class. - * @param isOptionalSet `true` if this property represents an optional set. - * @param isEditable `true` if the property's value is editable via the API. - */ -case class ClientPropertyDefinition(propertyName: String, - propertyDescription: Option[String], - propertyIri: SmartIri, - objectType: ClientObjectType, - cardinality: Cardinality, - isOptionalSet: Boolean, - isEditable: Boolean) - -/** - * A trait for types used in client API endpoints. - */ -sealed trait ClientObjectType - -/** - * A trait for datatypes. - */ -sealed trait ClientDatatype extends MapValueType with ArrayElementType - -/** - * The type of string datatype values. - */ -case object StringDatatype extends ClientDatatype with MapKeyDatatype - -/** - * The type of boolean datatype values. - */ -case object BooleanDatatype extends ClientDatatype - -/** - * The type of integer datatype values. - */ -case object IntegerDatatype extends ClientDatatype - -/** - * The type of decimal datatype values. - */ -case object DecimalDatatype extends ClientDatatype - -/** - * The type of URI datatype values. - */ -case object UriDatatype extends ClientDatatype with MapKeyDatatype - -/** - * The type of timestamp datatype values. - */ -case object DateTimeStampDatatype extends ClientDatatype - -/** - * A trait for types that may refer to a class IRI, either because the type represents that class, - * or because it is a collection type with a type variable referring to that class. - */ -sealed trait TypeWithClassIri { - /** - * Returns the class IRI, if any, that the type refers to. - */ - def getClassIri: Option[SmartIri] - - /** - * Returns a [[ClassRef]] representing the class IRI, if any, that the type refers to. - */ - def getClassRef: Option[ClassRef] = { - getClassIri.map { - classIri => ClassRef(className = classIri.getEntityName.capitalize, classIri = classIri) - } - } -} - -/** - * A trait for types representing collections in the target language. - */ -sealed trait CollectionType extends ClientObjectType with TypeWithClassIri - -/** - * A trait for types that can be keys in [[MapType]] data structures. - */ -sealed trait MapKeyDatatype extends ClientObjectType - -/** - * A trait for types that can be values in [[MapType]] data structures. - */ -sealed trait MapValueType extends ClientObjectType - -/** - * Represents a map-like data structure in the target language. - * - * @param keyType the type of the keys of the map. - * @param valueType the type of the values of the map. - */ -case class MapType(keyType: MapKeyDatatype, valueType: MapValueType) extends ClientObjectType with CollectionType with MapValueType with ArrayElementType { - override def getClassIri: Option[SmartIri] = valueType match { - case typeWithClassIri: TypeWithClassIri => typeWithClassIri.getClassIri - case _ => None - } -} - -/** - * A trait for types that can be elements in an [[ArrayType]] data structure. - */ -sealed trait ArrayElementType extends ClientObjectType - -/** - * Represents an array-like data structure in the target language. - * - * @param elementType the type of the elements of the array. - */ -case class ArrayType(elementType: ArrayElementType) extends ClientObjectType with CollectionType with MapValueType with ArrayElementType { - override def getClassIri: Option[SmartIri] = elementType match { - case typeWithClassIri: TypeWithClassIri => typeWithClassIri.getClassIri - case _ => None - } -} - -/** - * The type of enums. - * - * @param values the values of the enum. - */ -case class EnumDatatype(values: Set[String]) extends ClientDatatype - -/** - * The type of instances of classes. - * - * @param className the name of the class. - * @param classIri the IRI of the class. - */ -case class ClassRef(className: String, classIri: SmartIri) extends ClientObjectType with TypeWithClassIri with MapValueType with ArrayElementType { - /** - * Converts this [[ClassRef]] to one that represents a `Stored*` derived class. - */ - def toStoredClassRef: ClassRef = { - val storedClassName = ClassRef.makeStoredClassName(className) - val storedClassIri = classIri.getOntologyFromEntity.makeEntityIri(storedClassName) - ClassRef(className = storedClassName, classIri = storedClassIri) - } - - /** - * Converts this [[ClassRef]] to one that represents a `Read*` derived class. - */ - def toReadClassRef: ClassRef = { - val readClassName = ClassRef.makeReadClassName(className) - val readClassIri = classIri.getOntologyFromEntity.makeEntityIri(readClassName) - ClassRef(className = readClassName, classIri = readClassIri) - } - - override def getClassIri: Option[SmartIri] = Some(classIri) -} - -object ClassRef { - def isStoredClassName(className: String): Boolean = { - className.startsWith("Stored") - } - - def isReadClassName(className: String): Boolean = { - className.startsWith("Read") - } - - def isGeneratedDerivedClassName(className: String): Boolean = { - isStoredClassName(className) || isReadClassName(className) - } - - def toBaseClassName(className: String): String = { - if (isStoredClassName(className)) { - className.stripPrefix("Stored") - } else if (isReadClassName(className)) { - className.stripPrefix("Read") - } else { - className - } - } - - /** - * Converts a base class name to a `Stored*` derived class name. - */ - def makeStoredClassName(className: String): String = { - s"Stored$className" - } - - /** - * Converts a base class name to a `Read*` derived class name. - */ - def makeReadClassName(className: String): String = { - s"Read$className" - } -} - -/** - * A trait for Knora value types. - */ -sealed trait KnoraVal extends ClientObjectType - -/** - * The type of abstract Knora values. - */ -case object AbstractKnoraVal extends KnoraVal - -/** - * The type of text values. - */ -case object TextVal extends KnoraVal - -/** - * The type of integer values. - */ -case object IntVal extends KnoraVal - -/** - * The type of boolean values. - */ -case object BooleanVal extends KnoraVal - -/** - * The type of URI values. - */ -case object UriVal extends KnoraVal - -/** - * The type of decimal values. - */ -case object DecimalVal extends KnoraVal - -/** - * The type of date values. - */ -case object DateVal extends KnoraVal - -/** - * The type of color values. - */ -case object ColorVal extends KnoraVal - -/** - * The type of geometry values. - */ -case object GeomVal extends KnoraVal - -/** - * The type of list values. - */ -case object ListVal extends KnoraVal - -/** - * The type of interval values. - */ -case object IntervalVal extends KnoraVal - -/** - * The type of Geoname values. - */ -case object GeonameVal extends KnoraVal - -/** - * The type of audio file values. - */ -case object AudioFileVal extends KnoraVal - -/** - * The type of 3D file values. - */ -case object DDDFileVal extends KnoraVal - -/** - * The type of document file values. - */ -case object DocumentFileVal extends KnoraVal - -/** - * The type of still image file values. - */ -case object StillImageFileVal extends KnoraVal - -/** - * The type of moving image values. - */ -case object MovingImageFileVal extends KnoraVal - -/** - * The type of text file values. - */ -case object TextFileVal extends KnoraVal - -/** - * The type of link values. - * - * @param classIri the IRI of the class that is the target of the link. - */ -case class LinkVal(classIri: SmartIri) extends KnoraVal diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParser.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParser.scala deleted file mode 100644 index d32c0d7915..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParser.scala +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import org.knora.webapi.ClientApiGenerationException -import org.knora.webapi.util.{SmartIri, StringFormatter} - -/** - * Parses type annotations representing collection types for use in generated client code. - */ -object ClientCollectionTypeParser { - - /** - * Parses a collection type annotation. - * - * @param typeStr the type annotation to be parsed. - * @param ontologyIri the ontology IRI supplied with the type annotation. - * @return the parsed collection type annotation. - */ - def parse(typeStr: String, ontologyIri: SmartIri): CollectionType = { - implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - // Tokenise the input. - val tokens: Vector[TypeToken] = tokenise(typeStr) - - // Parse the type annotation. - val (objectType, remainingTokens) = parseType( - typeStr = typeStr, - tokens = tokens, - ontologyIri = ontologyIri - ) - - // Are there any tokens left? - if (remainingTokens.isEmpty) { - // No. Validate the collection type. - objectType match { - case collectionType: CollectionType => collectionType - case _ => throw ClientApiGenerationException(s"Expected a collection type: $typeStr") - } - } else { - throw ClientApiGenerationException(s"Invalid type: $typeStr") - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Token classes - - /** - * A token in a type annotation. - */ - private sealed trait TypeToken - - /** - * An identifier. - */ - private sealed trait Identifier extends TypeToken - - /** - * A built-in identifier. - */ - private sealed trait BuiltInIdentifier extends Identifier - - /** - * The name of a datatype. - */ - private trait DatatypeIdentifier extends BuiltInIdentifier { - def toClientDatatype: ClientDatatype - } - - /** - * The string datatype. - */ - private case object StringIdentifier extends DatatypeIdentifier { - override def toString: String = "String" - - override def toClientDatatype: ClientDatatype = StringDatatype - } - - /** - * The boolean datatype. - */ - private case object BooleanIdentifier extends DatatypeIdentifier { - override def toString: String = "Boolean" - - override def toClientDatatype: ClientDatatype = BooleanDatatype - } - - /** - * The integer datatype. - */ - private case object IntegerIdentifier extends DatatypeIdentifier { - override def toString: String = "Integer" - - override def toClientDatatype: ClientDatatype = IntegerDatatype - } - - /** - * The decimal datatype. - */ - private case object DecimalIdentifier extends DatatypeIdentifier { - override def toString: String = "Decimal" - - override def toClientDatatype: ClientDatatype = DecimalDatatype - } - - /** - * The URI datatype. - */ - private case object UriIdentifier extends DatatypeIdentifier { - override def toString: String = "URI" - - override def toClientDatatype: ClientDatatype = UriDatatype - } - - /** - * The `xsd:dateTimeStamp` datatype. - */ - private case object DateTimeStampIdentifier extends DatatypeIdentifier { - override def toString: String = "DateTimeStamp" - - override def toClientDatatype: ClientDatatype = DateTimeStampDatatype - } - - /** - * The `Map` collection type. - */ - private case object MapIdentifier extends BuiltInIdentifier { - override def toString: String = "Map" - } - - /** - * The `Array` collection type. - */ - private case object ArrayIdentifier extends BuiltInIdentifier { - override def toString: String = "Array" - } - - /** - * A map of identifier names to built-in identifiers. - */ - private val builtInIdentifiers: Map[String, BuiltInIdentifier] = Seq( - StringIdentifier, - BooleanIdentifier, - IntegerIdentifier, - DecimalIdentifier, - UriIdentifier, - DateTimeStampIdentifier, - MapIdentifier, - ArrayIdentifier - ).map { - token => token.toString -> token - }.toMap - - /** - * A token representing punctuation. - */ - private sealed trait PunctuationToken extends TypeToken { - def toChar: Char - } - - /** - * The `[` token. - */ - private case object OpenBracketToken extends PunctuationToken { - def toChar: Char = '[' - } - - /** - * The `]` token. - */ - private case object CloseBracketToken extends PunctuationToken { - def toChar: Char = ']' - } - - /** - * The `,` token. - */ - private case object CommaToken extends PunctuationToken { - def toChar: Char = ',' - } - - /** - * A map of characters to punctuation tokens. - */ - private val punctuation: Map[Char, PunctuationToken] = Seq( - OpenBracketToken, - CloseBracketToken, - CommaToken - ).map { - token => token.toChar -> token - }.toMap - - /** - * An identifier representing a class name in the target language. - */ - private case class ClassRefIdentifier(className: String) extends Identifier { - override def toString: String = className - - /** - * Converts this token to a [[ClassRef]], using the specified ontology IRI. - * - * @param ontologyIri the IRI of the ontology supplied with the type annotation. - */ - def toClassRef(ontologyIri: SmartIri)(implicit stringFormatter: StringFormatter): ClassRef = { - ClassRef(className = className, classIri = ontologyIri.makeEntityIri(className)) - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Tokeniser - - /** - * Tokenises a type annotation. - * - * @param typeStr the string to be tokenised. - * @return the tokens representing the type annotation. - */ - private def tokenise(typeStr: String): Vector[TypeToken] = { - tokeniseRec( - typeStr = typeStr, - pos = 0, - currentIdentifier = "", - tokens = Vector.empty - ) - } - - /** - * Recursively tokenises a type annotation. - * - * @param typeStr the string to be tokenised. - * @param pos the current position in the string. - * @param currentIdentifier the identifier currently being tokenised. - * @param tokens the tokens that have been collected so far. - * @return the tokens representing the type annotation. - */ - @scala.annotation.tailrec - private def tokeniseRec(typeStr: String, pos: Int, currentIdentifier: String, tokens: Vector[TypeToken]): Vector[TypeToken] = { - /** - * Returns the tokens that have been collected so far, including a token representing `currentIdentifier` - * if it is not empty. - */ - def collectTokens: Vector[TypeToken] = { - // Are there characters in currentIdentifier? - if (currentIdentifier.nonEmpty) { - // Yes. Is it a built-in identifier? - val identifierToken = builtInIdentifiers.get(currentIdentifier) match { - case Some(builtInIdentifier) => - // Yes. - builtInIdentifier - - case None => - // No. It must be a class reference. - ClassRefIdentifier(currentIdentifier) - } - - // Return the tokens collected so far, plus the identifier token. - tokens :+ identifierToken - } else { - // There are no characters in currentIdentifier. Just return the tokens collected so far. - tokens - } - } - - // Are we at the end of the input? - if (pos == typeStr.length) { - // Yes. Return the tokens collected so far, including currentIdentifier if it is not empty. - collectTokens - } else { - // Get the next character. - val char: Char = typeStr.charAt(pos) - - // Skip whitespace. - if (char == ' ') { - tokeniseRec( - typeStr = typeStr, - pos = pos + 1, - currentIdentifier = currentIdentifier, - tokens = tokens - ) - } else { - // Is this a punctuation character? - punctuation.get(char) match { - case Some(punctuationToken) => - // Yes. Get any identifier preceding the punctuation, add the punctuation token, - // and recurse. - val collectedTokens = collectTokens :+ punctuationToken - - tokeniseRec( - typeStr = typeStr, - pos = pos + 1, - currentIdentifier = "", - tokens = collectedTokens - ) - - case None => - // This is not a punctuation character. Is it a Unicode letter? - if (Character.isLetter(char)) { - // Yes. It must be part of an identifier. Recurse. - tokeniseRec( - typeStr = typeStr, - pos = pos + 1, - currentIdentifier = currentIdentifier + char, - tokens = tokens - ) - } else { - // It's not a Unicode letter, so it's an invalid character. - throw ClientApiGenerationException(s"Unexpected character '$char' in object type name: $typeStr") - } - } - } - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Parser - - /** - * Recursively parses a type annotation. - * - * @param typeStr the string representation of the type annotation being parsed. - * @param tokens the tokens not yet parsed. - * @param ontologyIri the IRI of the ontology supplied with the type annotation. - * @return the parsed type annotation and the tokens that follow it. - */ - private def parseType(typeStr: String, tokens: Vector[TypeToken], ontologyIri: SmartIri)(implicit stringFormatter: StringFormatter): (ClientObjectType, Vector[TypeToken]) = { - tokens.headOption match { - case Some(token) => - token match { - case MapIdentifier => - parseMap( - typeStr = typeStr, - tokens = tokens.tail, - ontologyIri = ontologyIri - ) - - case ArrayIdentifier => - parseArray( - typeStr = typeStr, - tokens = tokens.tail, - ontologyIri = ontologyIri - ) - - case datatypeToken: DatatypeIdentifier => (datatypeToken.toClientDatatype, tokens.tail) - - case classRefToken: ClassRefIdentifier => (classRefToken.toClassRef(ontologyIri), tokens.tail) - - case _ => throw ClientApiGenerationException(s"Invalid type: $typeStr") - } - - case None => throw ClientApiGenerationException(s"Invalid type: $typeStr") - } - } - - /** - * Parses a `Map` type annotation. - * - * @param typeStr the string representation of the type annotation being parsed. - * @param tokens the tokens not yet parsed. - * @param ontologyIri the IRI of the ontology supplied with the type annotation. - * @return a [[MapType]] and the tokens that follow it. - */ - private def parseMap(typeStr: String, tokens: Vector[TypeToken], ontologyIri: SmartIri)(implicit stringFormatter: StringFormatter): (MapType, Vector[TypeToken]) = { - // Consume the open bracket. - val afterOpenBracket: Vector[TypeToken] = consumePunctuation( - typeStr = typeStr, - tokens = tokens, - punctuationToken = OpenBracketToken - ) - - // Get the key type. - val (keyType: ClientObjectType, afterKeyType: Vector[TypeToken]) = parseType( - typeStr = typeStr, - tokens = afterOpenBracket, - ontologyIri = ontologyIri - ) - - // Validate the key type. - val validKeyType: MapKeyDatatype = keyType match { - case mapKeyDatatype: MapKeyDatatype => mapKeyDatatype - case _ => throw ClientApiGenerationException(s"Invalid map key type: $typeStr") - } - - // Consume the comma separating the key type from the value type. - val afterComma: Vector[TypeToken] = consumePunctuation( - typeStr = typeStr, - tokens = afterKeyType, - punctuationToken = CommaToken - ) - - // Get the value type. - val (valueType: ClientObjectType, afterValueType: Vector[TypeToken]) = parseType( - typeStr = typeStr, - tokens = afterComma, - ontologyIri = ontologyIri - ) - - // Validate the value type. - val validValueType: MapValueType = valueType match { - case mapValueType: MapValueType => mapValueType - case _ => throw ClientApiGenerationException(s"Invalid map value type: $typeStr") - } - - // Consume the close bracket. - val afterCloseBracket = consumePunctuation( - typeStr = typeStr, - tokens = afterValueType, - punctuationToken = CloseBracketToken - ) - - (MapType(keyType = validKeyType, valueType = validValueType), afterCloseBracket) - } - - /** - * Parses an `Array` type annotation. - * - * @param typeStr the string representation of the type annotation being parsed. - * @param tokens the tokens not yet parsed. - * @param ontologyIri the IRI of the ontology supplied with the type annotation. - * @return a [[ArrayType]] and the tokens that follow it. - */ - private def parseArray(typeStr: String, tokens: Vector[TypeToken], ontologyIri: SmartIri)(implicit stringFormatter: StringFormatter): (ArrayType, Vector[TypeToken]) = { - // Consume the open bracket. - val afterOpenBracket: Vector[TypeToken] = consumePunctuation( - typeStr = typeStr, - tokens = tokens, - punctuationToken = OpenBracketToken - ) - - // Get the element type. - val (elementType: ClientObjectType, afterKeyType: Vector[TypeToken]) = parseType( - typeStr = typeStr, - tokens = afterOpenBracket, - ontologyIri = ontologyIri - ) - - // Validate the element type. - val validElementType = elementType match { - case arrayElementType: ArrayElementType => arrayElementType - case _ => throw ClientApiGenerationException(s"Invalid array element type: $typeStr") - } - - // Consume the close bracket. - val afterCloseBracket = consumePunctuation( - typeStr = typeStr, - tokens = afterKeyType, - punctuationToken = CloseBracketToken - ) - - (ArrayType(elementType = validElementType), afterCloseBracket) - } - - /** - * Consumes a punctuation token. - * - * @param typeStr the string representation of the type annotation being parsed. - * @param tokens the tokens not yet parsed. - * @param punctuationToken the expected punctuation token. - * @return the tokens following the punctuation token. - */ - private def consumePunctuation(typeStr: String, tokens: Vector[TypeToken], punctuationToken: PunctuationToken): Vector[TypeToken] = { - // Is there a next token? - tokens.headOption match { - case Some(token) => - // Yes. Is it the expected punctuation token? - if (token == punctuationToken) { - // Yes. Return the tokens following the punctuation token. - tokens.tail - } else { - throw ClientApiGenerationException(s"Expected '${punctuationToken.toChar}': $typeStr") - } - - case None => throw ClientApiGenerationException(s"Expected '${punctuationToken.toChar}': $typeStr") - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorBackEnd.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorBackEnd.scala deleted file mode 100644 index b72632d66f..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorBackEnd.scala +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import scala.annotation.tailrec - -/** - * Represents a client API as input to the generator back end. - * - * @param apiDef the API definition. - * @param clientClassDefs the class definitions used in the API. - */ -case class ClientApiBackendInput(apiDef: ClientApi, clientClassDefs: Set[ClientClassDefinition]) - -/** - * Represents the filesystem path of a file containing generated source code. - * - * @param directoryPath the path of the directory containing the file, - * relative to the root directory of the source tree. - * @param filename the filename, without the file extension. - * @param fileExtension the file extension. - */ -case class SourceCodeFilePath(directoryPath: Seq[String], filename: String, fileExtension: String) { - /** - * Given the [[SourceCodeFilePath]] of a file to be imported, returns that path relative to this - * [[SourceCodeFilePath]]. - * - * @param thatSourceCodeFilePath the path of the file to be imported. - * @param includeFileExtension if `true`, include the imported file's extension in the result. - * @return a relative file path for importing `thatSourceCodeFilePath` in the file represented by this - * [[SourceCodeFilePath]]. - */ - def makeImportPath(thatSourceCodeFilePath: SourceCodeFilePath, includeFileExtension: Boolean = true): String = { - // Find the first common parent directory. - val (thisPathFromCommonParent, thatPathFromCommonParent) = SourceCodeFilePath.stripDirsUntilDifferent( - directoryPath, - thatSourceCodeFilePath.directoryPath - ) - - // Make a relative path for walking up the directory tree to the first common parent directory, - // then down to the target directory. - val importDirectoryPath: Seq[String] = thisPathFromCommonParent.map(_ => "..") ++ thatPathFromCommonParent - - val importFileExtension = if (includeFileExtension) { - thatSourceCodeFilePath.fileExtension - } else { - "" - } - - thatSourceCodeFilePath.copy( - directoryPath = importDirectoryPath, - fileExtension = importFileExtension - ).toString - } - - override def toString: String = { - val directoryPathWithCurrentDir = directoryPath.headOption match { - case Some("..") => directoryPath - case _ => "." +: directoryPath - } - - val filePathWithoutExtension: String = (directoryPathWithCurrentDir :+ filename).mkString("/") - - if (fileExtension.isEmpty) { - filePathWithoutExtension - } else { - filePathWithoutExtension + "." + fileExtension - } - } -} - -object SourceCodeFilePath { - /** - * Given two paths that are both relative to the root directory of the source tree, strips leading - * directories until the paths diverge or at least one of them terminates. - * - * @param thisPath the path of the importing file. - * @param thatPath the path of the imported file. - * @return the diverging parts of the two paths. - */ - @tailrec - private def stripDirsUntilDifferent(thisPath: Seq[String], thatPath: Seq[String]): (Seq[String], Seq[String]) = { - if (thisPath.isEmpty || thatPath.isEmpty || thisPath.head != thatPath.head) { - (thisPath, thatPath) - } else { - stripDirsUntilDifferent(thisPath.tail, thatPath.tail) - } - } - - /** - * A convenience method that makes a path for a JSON file in the current directory. - * - * @param filename the filename. - * @return the file path. - */ - def makeJsonPath(filename: String): SourceCodeFilePath = { - SourceCodeFilePath( - directoryPath = Seq.empty, - filename = filename, - fileExtension = "json" - ) - } - - /** - * A convenience method that makes a path for a JSON-LD file in the current directory. - * - * @param filename the filename. - * @return the file path. - */ - def makeJsonLDPath(filename: String): SourceCodeFilePath = { - SourceCodeFilePath( - directoryPath = Seq.empty, - filename = filename, - fileExtension = "jsonld" - ) - } -} - -/** - * Represents a file containing generated client API source code. - * - * @param filePath the filename in which the source code should be saved. - * @param text the source code. - */ -case class SourceCodeFileContent(filePath: SourceCodeFilePath, text: String) - -/** - * A trait for client API code generator back ends. A back end is responsible for producing client API library - * source code in a particular programming language. - */ -trait GeneratorBackEnd { - /** - * Generates client API source code. - * - * @param apis the APIs from which source code is to be generated. - * @param params parameters to configure source code generation. - * @return the generated source code. - */ - def generateClientSourceCode(apis: Set[ClientApiBackendInput], params: Map[String, String]): Set[SourceCodeFileContent] -} diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala deleted file mode 100644 index a5e6fc7061..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import akka.actor.{ActorRef, ActorSystem} -import akka.http.scaladsl.util.FastFuture -import akka.pattern._ -import akka.util.Timeout -import org.knora.webapi._ -import org.knora.webapi.messages.admin.responder.usersmessages.UserADM -import org.knora.webapi.messages.v2.responder.ontologymessages._ -import org.knora.webapi.routing.KnoraRouteData -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.jsonld.JsonLDUtil -import org.knora.webapi.util.{SmartIri, StringFormatter} - -import scala.concurrent.{ExecutionContext, Future} - -/** - * The front end of the client code generator. It is responsible for producing [[ClientClassDefinition]] objects - * representing the Knora classes used in an API. - */ -class GeneratorFrontEnd(routeData: KnoraRouteData, requestingUser: UserADM) { - implicit private val system: ActorSystem = routeData.system - implicit private val responderManager: ActorRef = routeData.appActor - implicit private val settings: SettingsImpl = Settings(system) - implicit private val timeout: Timeout = settings.defaultTimeout - implicit private val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - // Return code documentation in English. - private val userWithLang = requestingUser.copy(lang = LanguageCodes.EN) - - /** - * Returns a set of [[ClientClassDefinition]] instances representing the Knora classes used by a client API. - */ - def getClientClassDefs(clientApi: ClientApi): Future[Set[ClientClassDefinition]] = { - /** - * An accumulator for client class definitions and ontologies. - * - * @param clientDefs the client class definitions accumulated so far. - * @param ontologies the ontologies accumulated so far. - */ - case class ClientDefsWithOntologies(clientDefs: Map[SmartIri, ClientClassDefinition], ontologies: Map[SmartIri, InputOntologyV2]) - - /** - * Recursively gets class definitions. - * - * @param classIri the IRI of a class whose definition is needed. - * @param definitionAcc the class definitions and ontologies collected so far. - * @return the class definitions and ontologies resulting from recursion. - */ - def getClassDefsRec(classIri: SmartIri, definitionAcc: ClientDefsWithOntologies): Future[ClientDefsWithOntologies] = { - val className = classIri.getEntityName - - // Is this the IRI of a derived class that hasn't been generated yet? - if (ClassRef.isGeneratedDerivedClassName(className)) { - // Yes. Do we already have the definition of the base class? - - val baseClassIri: SmartIri = classIri.getOntologyFromEntity.makeEntityIri(ClassRef.toBaseClassName(className)) - - if (definitionAcc.clientDefs.contains(baseClassIri)) { - // Yes. Return. - FastFuture.successful(definitionAcc) - } else { - // No. Recurse to get it. - getClassDefsRec(baseClassIri, definitionAcc) - } - } else { - // Get the IRI of the ontology containing the class. - val classOntologyIri: SmartIri = classIri.getOntologyFromEntity - - for { - // Get the ontology containing the class. - ontologiesWithClassOntology <- getOntology(classOntologyIri, definitionAcc.ontologies) - classOntology = ontologiesWithClassOntology(classOntologyIri) - - // Get the RDF definition of the class. - rdfClassDef: ClassInfoContentV2 = classOntology.classes.getOrElse(classIri, throw ClientApiGenerationException(s"Class <$classIri> not found")) - - // Get the IRIs of the class's Knora properties. - rdfPropertyIris: Set[SmartIri] = rdfClassDef.directCardinalities.keySet.filter { - propertyIri => propertyIri.isKnoraEntityIri - } - - // Get the ontologies containing the definitions of those properties. - ontologiesWithPropertyDefs: Map[SmartIri, InputOntologyV2] <- rdfPropertyIris.foldLeft(FastFuture.successful(ontologiesWithClassOntology)) { - case (acc, propertyIri) => - val propertyOntologyIri = propertyIri.getOntologyFromEntity - - for { - currentOntologies: Map[SmartIri, InputOntologyV2] <- acc - ontologiesWithPropertyDef: Map[SmartIri, InputOntologyV2] <- getOntology(propertyOntologyIri, currentOntologies) - } yield ontologiesWithPropertyDef - } - - // Get the definitions of the properties. - rdfPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = rdfPropertyIris.map { - propertyIri => - val ontology = ontologiesWithPropertyDefs(propertyIri.getOntologyFromEntity) - propertyIri -> ontology.properties.getOrElse(propertyIri, throw ClientApiGenerationException(s"Property <$propertyIri> not found")) - }.toMap - - // Convert the RDF class definition into a ClientClassDefinition. - clientClassDef: ClientClassDefinition = rdfClassDef2ClientClassDef( - clientApi = clientApi, - rdfClassDef = rdfClassDef, - rdfPropertyDefs = rdfPropertyDefs - ) - - // Make a new ClientDefsWithOntologies including that ClientClassDefinition as well as the ontologies - // we've collected. - accForRecursion = ClientDefsWithOntologies( - clientDefs = definitionAcc.clientDefs + (clientClassDef.classIri -> clientClassDef), - ontologies = ontologiesWithPropertyDefs - ) - - // Recursively get definitions of classes used as property object types. - accFromRecursion: ClientDefsWithOntologies <- clientClassDef.properties.foldLeft(FastFuture.successful(accForRecursion)) { - case (acc: Future[ClientDefsWithOntologies], clientPropertyDef: ClientPropertyDefinition) => - // Does this property have something containing a class IRI as its object type - // (a ClassRef, or a CollectionType referring to a class IRI)? - clientPropertyDef.objectType match { - case typeWithClassIri: TypeWithClassIri => - typeWithClassIri.getClassIri match { - case Some(objectTypeClassIri) => - // Yes. - for { - currentAcc: ClientDefsWithOntologies <- acc - - // Do we have this class definition already? - newAcc: ClientDefsWithOntologies <- if (currentAcc.clientDefs.contains(objectTypeClassIri)) { - // Yes. Nothing to do. - acc - } else { - // No. Recurse to get it. - for { - recursionResults: ClientDefsWithOntologies <- getClassDefsRec( - classIri = objectTypeClassIri, - definitionAcc = currentAcc - ) - } yield recursionResults - } - } yield newAcc - - case None => - // This property doesn't have an object type containing a class IRI. - acc - } - - // This property doesn't have an object type containing a class IRI. - case _ => acc - } - } - } yield accFromRecursion - } - } - - // Iterate over all the class IRIs used in the client API, making a client definition for each class, - // as well as for any other classes referred to by that class. - - val initialAcc = ClientDefsWithOntologies(clientDefs = Map.empty, ontologies = Map.empty) - - for { - allDefs: ClientDefsWithOntologies <- clientApi.classIrisUsed.foldLeft(FastFuture.successful(initialAcc)) { - case (acc: Future[ClientDefsWithOntologies], classIri) => - for { - currentAcc: ClientDefsWithOntologies <- acc - - recursionResults: ClientDefsWithOntologies <- getClassDefsRec( - classIri = classIri, - definitionAcc = currentAcc - ) - } yield recursionResults - } - } yield transformClientClassDefs( - clientApi = clientApi, - clientDefs = allDefs.clientDefs - ) - } - - /** - * Transforms client class definitions by adding `Stored*` and `Read*` classes, transforming the properties of their base - * classes, and transforming the properties of response classes. - * - * @param clientApi the client API definition. - * @param clientDefs the client class definitions used by the API. - * @return the transformed class definitions. - */ - private def transformClientClassDefs(clientApi: ClientApi, clientDefs: Map[SmartIri, ClientClassDefinition]): Set[ClientClassDefinition] = { - /** - * Transforms the class reference object types of properties. - * - * @param properties the properties to transform. - * @param classIrisToReplace A map of source class IRIs to replacement class IRIs. - * @return the transformed properties. - */ - def transformPropertyObjectTypes(properties: Vector[ClientPropertyDefinition], - classIrisToReplace: Map[SmartIri, SmartIri]): Vector[ClientPropertyDefinition] = { - properties.map { - propDef: ClientPropertyDefinition => - propDef.objectType match { - case classRef: ClassRef => - classIrisToReplace.get(classRef.classIri) match { - case Some(targetClassIri) => - propDef.copy( - objectType = ClassRef(className = makeClientClassName(targetClassIri), classIri = targetClassIri) - ) - - case None => propDef - } - - case _ => propDef - } - } - } - - // Rename properties as requested. - val classesWithRenamedProps: Map[SmartIri, ClientClassDefinition] = clientDefs.map { - case (classIri: SmartIri, classDef: ClientClassDefinition) => - val transformedProps = classDef.properties.map { - propDef => - clientApi.propertyNames.get(classIri) match { - case Some(propertyMap) => - propertyMap.get(propDef.propertyIri) match { - case Some(propertyName) => propDef.copy(propertyName = propertyName) - case None => propDef - } - - case None => propDef - } - } - - classIri -> classDef.copy(properties = transformedProps) - } - - // A class that has an ID property needs a Stored* subclass. - val classIrisNeedingStoredClasses: Set[SmartIri] = classesWithRenamedProps.collect { - case (classIri, classDef) if classDef.properties.exists(propDef => clientApi.idProperties.contains(propDef.propertyIri)) => classIri - }.toSet -- clientApi.requestClasses -- clientApi.readOnlyClasses - - // A map of the IRIs of classes that need Stored* classes, to the IRIs of their Stored* classes. - val storedClassIris: Map[SmartIri, SmartIri] = classIrisNeedingStoredClasses.map { - classIri => classIri -> classIri.getOntologyFromEntity.makeEntityIri(ClassRef.makeStoredClassName(classIri.getEntityName)) - }.toMap - - // A class that has one or more read-only properties needs a Read* subclass. - val classIrisNeedingReadClasses: Set[SmartIri] = clientApi.classesWithReadOnlyProperties.keySet -- clientApi.readOnlyClasses - - // A map of the IRIs of classes that need Read* classes, to the IRIs of their Read* classes. - val readClassIris: Map[SmartIri, SmartIri] = classIrisNeedingReadClasses.map { - classIri => classIri -> classIri.getOntologyFromEntity.makeEntityIri(ClassRef.makeReadClassName(classIri.getEntityName)) - }.toMap - - val classesNeedingStoredClasses: Map[SmartIri, ClientClassDefinition] = classesWithRenamedProps.filterKeys(classIrisNeedingStoredClasses) - - val generatedSubclassesAndTransformedBaseClasses: Map[SmartIri, ClientClassDefinition] = classesNeedingStoredClasses.flatMap { - case (classIri: SmartIri, classDef: ClientClassDefinition) => - // A Stored* class contains only the ID property from the base class. The ID property's cardinality - // in the Stored* class is MustHaveOne. - val propsForStoredClass: Vector[ClientPropertyDefinition] = classDef.properties.collect { - case propDef if clientApi.idProperties.contains(propDef.propertyIri) => - propDef.copy(cardinality = Cardinality.MustHaveOne) - } - - if (propsForStoredClass.length > 1) { - throw ClientApiGenerationException(s"Class $classIri has more than one ID property: ${propsForStoredClass.map(_.propertyIri).mkString(", ")}") - } - - val storedClassIri = storedClassIris(classIri) - - val storedClassDef: ClientClassDefinition = classDef.copy( - className = storedClassIri.getEntityName, - classIri = storedClassIri, - properties = propsForStoredClass, - subClassOf = Some(classDef.classIri) - ) - - val readOnlyPropertyIris: Set[SmartIri] = clientApi.classesWithReadOnlyProperties.getOrElse(classDef.classIri, Set.empty) - - val maybeReadClassIriAndDef: Option[(SmartIri, ClientClassDefinition)] = if (classIrisNeedingReadClasses.contains(classIri)) { - // In a Read* class, only read-only properties should be included. - val readOnlyPropsInClass: Vector[ClientPropertyDefinition] = classDef.properties.filter { - propDef => readOnlyPropertyIris.contains(propDef.propertyIri) - } - - // To avoid circular dependencies, the read-only properties should point to Stored* classes. - val propsForReadClass = transformPropertyObjectTypes( - properties = readOnlyPropsInClass, - classIrisToReplace = storedClassIris - ) - - val readClassIri = readClassIris(classIri) - - val readClassDef: ClientClassDefinition = classDef.copy( - className = readClassIri.getEntityName, - classIri = readClassIri, - properties = propsForReadClass, - subClassOf = Some(storedClassIri) - ) - - Some(readClassIri -> readClassDef) - } else { - None - } - - // Remove the ID property and read-only properties from the transformed base class. - - val baseClassDef: ClientClassDefinition = classDef.copy( - properties = classDef.properties.filterNot { - propDef => - clientApi.idProperties.contains(propDef.propertyIri) || readOnlyPropertyIris.contains(propDef.propertyIri) - } - ) - - Map( - baseClassDef.classIri -> baseClassDef, - storedClassIri -> storedClassDef - ) ++ maybeReadClassIriAndDef - } - - // In a response class, every property should have another Read* class (if available) as its object type. - - val responseClasses: Map[SmartIri, ClientClassDefinition] = classesWithRenamedProps.filterKeys(clientApi.responseClasses) - - val transformedResponseClasses: Map[SmartIri, ClientClassDefinition] = responseClasses.map { - case (classIri: SmartIri, classDef: ClientClassDefinition) => - val transformedProps = transformPropertyObjectTypes( - properties = classDef.properties, - classIrisToReplace = readClassIris - ) - - classIri -> classDef.copy( - properties = transformedProps - ) - } - - (classesWithRenamedProps ++ generatedSubclassesAndTransformedBaseClasses ++ transformedResponseClasses).values.toSet - } - - /** - * Gets an ontology from Knora. - * - * @param ontologyIri the IRI of the ontology. - * @param ontologies the ontologies collected so far. - * @return `ontologies` plus the requested ontology. - */ - private def getOntology(ontologyIri: SmartIri, ontologies: Map[SmartIri, InputOntologyV2]): Future[Map[SmartIri, InputOntologyV2]] = { - val requestMessage = OntologyEntitiesGetRequestV2( - ontologyIri = ontologyIri, - allLanguages = false, - requestingUser = userWithLang - ) - - for { - ontologiesResponse: ReadOntologyV2 <- (responderManager ? requestMessage).mapTo[ReadOntologyV2] - - responseAsJsonLD = ontologiesResponse.toJsonLDDocument( - targetSchema = ApiV2Complex, - settings = settings, - schemaOptions = Set.empty - ).toCompactString - - ontology: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(responseAsJsonLD)).unescape - } yield ontologies + (ontologyIri -> ontology) - } - - /** - * Converts RDF class definitions into [[ClientClassDefinition]] instances. - * - * @param clientApi the client API definition. - * @param rdfClassDef the class definition to be converted. - * @param rdfPropertyDefs the definitions of the properties used in the class. - * @return a [[ClientClassDefinition]] describing the class. - */ - private def rdfClassDef2ClientClassDef(clientApi: ClientApi, rdfClassDef: ClassInfoContentV2, rdfPropertyDefs: Map[SmartIri, PropertyInfoContentV2]): ClientClassDefinition = { - implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - val isResourceClass = rdfClassDef.getPredicateBooleanObject(OntologyConstants.KnoraApiV2Complex.IsResourceClass.toSmartIri) - - if (isResourceClass) { - rdfResourceClassDef2ClientClassDef( - clientApi = clientApi, - rdfClassDef = rdfClassDef, - rdfPropertyDefs = rdfPropertyDefs - ) - } else { - rdfNonResourceClassDef2ClientClassDef( - clientApi = clientApi, - rdfClassDef = rdfClassDef, - rdfPropertyDefs = rdfPropertyDefs - ) - } - } - - /** - * Converts Knora resource class definitions into [[ClientClassDefinition]] instances. - * - * @param clientApi the client API definition. - * @param rdfClassDef the class definition to be converted. - * @param rdfPropertyDefs the definitions of the properties used in the class. - * @return a [[ClientClassDefinition]] describing the class. - */ - private def rdfResourceClassDef2ClientClassDef(clientApi: ClientApi, rdfClassDef: ClassInfoContentV2, rdfPropertyDefs: Map[SmartIri, PropertyInfoContentV2]): ClientClassDefinition = { - implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - val classDescription: Option[String] = rdfClassDef.getPredicateStringLiteralObject(OntologyConstants.Rdfs.Comment.toSmartIri) - - val cardinalitiesWithoutLinkProps = rdfClassDef.directCardinalities.filter { - case (propertyIri, _) => - rdfPropertyDefs.get(propertyIri) match { - case Some(rdfPropertyDef) => !rdfPropertyDef.getPredicateBooleanObject(OntologyConstants.KnoraApiV2Complex.IsLinkProperty.toSmartIri) - case None => true - } - } - - val clientPropertyDefs: Vector[ClientPropertyDefinition] = cardinalitiesWithoutLinkProps.map { - case (propertyIri, knoraCardinalityInfo) => - val propertyName = propertyIri.getEntityName - - val isOptionalSet = isOptionalSetProperty( - clientApi = clientApi, - classIri = rdfClassDef.classIri, - propertyIri = propertyIri - ) - - if (propertyIri.isKnoraEntityIri) { - val rdfPropertyDef = rdfPropertyDefs(propertyIri) - val propertyDescription: Option[String] = rdfPropertyDef.getPredicateStringLiteralObject(OntologyConstants.Rdfs.Comment.toSmartIri) - val ontologyObjectType: SmartIri = rdfPropertyDef.requireIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri, throw InconsistentTriplestoreDataException(s"Property $propertyIri has no knora-api:objectType")) - val isResourceProp = rdfPropertyDef.getPredicateBooleanObject(OntologyConstants.KnoraApiV2Complex.IsResourceProperty.toSmartIri) - val isEditable = rdfPropertyDef.getPredicateBooleanObject(OntologyConstants.KnoraApiV2Complex.IsEditable.toSmartIri) - - if (isResourceProp) { - val isLinkValueProp = rdfPropertyDef.getPredicateBooleanObject(OntologyConstants.KnoraApiV2Complex.IsLinkValueProperty.toSmartIri) - - val clientObjectType: ClientObjectType = if (isLinkValueProp) { - LinkVal(ontologyObjectType) - } else { - resourcePropObjectTypeToClientObjectType(ontologyObjectType) - } - - ClientPropertyDefinition( - propertyName = propertyName, - propertyDescription = propertyDescription, - propertyIri = propertyIri, - objectType = clientObjectType, - cardinality = knoraCardinalityInfo.cardinality, - isOptionalSet = isOptionalSet, - isEditable = isEditable - ) - } else { - ClientPropertyDefinition( - propertyName = propertyName, - propertyDescription = propertyDescription, - propertyIri = propertyIri, - objectType = nonResourcePropObjectTypeToClientObjectType(ontologyObjectType), - cardinality = knoraCardinalityInfo.cardinality, - isOptionalSet = isOptionalSet, - isEditable = isEditable - ) - } - } else { - ClientPropertyDefinition( - propertyName = propertyName, - propertyDescription = None, - propertyIri = propertyIri, - objectType = StringDatatype, - cardinality = knoraCardinalityInfo.cardinality, - isOptionalSet = isOptionalSet, - isEditable = propertyIri.toString == OntologyConstants.Rdfs.Label // Labels of resources are editable - ) - } - }.toVector.sortBy(_.propertyIri) - - ClientClassDefinition( - className = makeClientClassName(rdfClassDef.classIri), - classDescription = classDescription, - classIri = rdfClassDef.classIri, - properties = clientPropertyDefs.sortBy(_.propertyIri) - ) - } - - /** - * Converts Knora non-resource class definitions into [[ClientClassDefinition]] instances. - * - * @param clientApi the client API definition. - * @param rdfClassDef the class definition to be converted. - * @param rdfPropertyDefs the definitions of the properties used in the class. - * @return a [[ClientClassDefinition]] describing the class. - */ - private def rdfNonResourceClassDef2ClientClassDef(clientApi: ClientApi, rdfClassDef: ClassInfoContentV2, rdfPropertyDefs: Map[SmartIri, PropertyInfoContentV2]): ClientClassDefinition = { - implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - val classDescription: Option[String] = rdfClassDef.getPredicateStringLiteralObject(OntologyConstants.Rdfs.Comment.toSmartIri) - - val clientPropertyDefs = rdfClassDef.directCardinalities.map { - case (propertyIri, knoraCardinalityInfo) => - val propertyName = propertyIri.getEntityName - - val isOptionalSet = isOptionalSetProperty( - clientApi = clientApi, - classIri = rdfClassDef.classIri, - propertyIri = propertyIri - ) - - if (propertyIri.isKnoraEntityIri) { - val rdfPropertyDef = rdfPropertyDefs(propertyIri) - val propertyDescription: Option[String] = rdfPropertyDef.getPredicateStringLiteralObject(OntologyConstants.Rdfs.Comment.toSmartIri) - val ontologyObjectType: SmartIri = rdfPropertyDef.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri).getOrElse(OntologyConstants.Xsd.String.toSmartIri) - - ClientPropertyDefinition( - propertyName = propertyName, - propertyDescription = propertyDescription, - propertyIri = propertyIri, - objectType = nonResourcePropObjectTypeToClientObjectType(ontologyObjectType), - cardinality = knoraCardinalityInfo.cardinality, - isOptionalSet = isOptionalSet, - isEditable = true - ) - } else { - ClientPropertyDefinition( - propertyName = propertyName, - propertyDescription = None, - propertyIri = propertyIri, - objectType = StringDatatype, - cardinality = knoraCardinalityInfo.cardinality, - isOptionalSet = isOptionalSet, - isEditable = true - ) - } - }.toVector.sortBy(_.propertyIri) - - ClientClassDefinition( - className = makeClientClassName(rdfClassDef.classIri), - classDescription = classDescription, - classIri = rdfClassDef.classIri, - properties = clientPropertyDefs.sortBy(_.propertyIri) - ) - } - - /** - * Returns `true` if the specified property IRI is an optional set property in the specified class. - * - * @param clientApi the client API definition. - * @param classIri the class IRI. - * @param propertyIri the property IRI. - * @return - */ - private def isOptionalSetProperty(clientApi: ClientApi, classIri: SmartIri, propertyIri: SmartIri): Boolean = { - clientApi.classesWithOptionalSetProperties.get(classIri).exists(_.contains(propertyIri)) - } - - /** - * Given the IRI of an RDF class, creates a client class name for it. - * - * @param classIri the class IRI. - * @return the client class name. - */ - private def makeClientClassName(classIri: SmartIri): String = { - classIri.getEntityName.capitalize - } - - /** - * Given a Knora value object type, returns the corresponding [[ClientObjectType]]. - * - * @param ontologyObjectType the RDF type. - * @return the corresponding [[ClientObjectType]]. - */ - private def resourcePropObjectTypeToClientObjectType(ontologyObjectType: SmartIri): ClientObjectType = { - ontologyObjectType.toString match { - case OntologyConstants.KnoraApiV2Complex.Value => AbstractKnoraVal - case OntologyConstants.KnoraApiV2Complex.TextValue => TextVal - case OntologyConstants.KnoraApiV2Complex.IntValue => IntVal - case OntologyConstants.KnoraApiV2Complex.DecimalValue => DecimalVal - case OntologyConstants.KnoraApiV2Complex.BooleanValue => BooleanVal - case OntologyConstants.KnoraApiV2Complex.DateValue => DateVal - case OntologyConstants.KnoraApiV2Complex.GeomValue => GeomVal - case OntologyConstants.KnoraApiV2Complex.IntervalValue => IntervalVal - case OntologyConstants.KnoraApiV2Complex.ListValue => ListVal - case OntologyConstants.KnoraApiV2Complex.UriValue => UriVal - case OntologyConstants.KnoraApiV2Complex.GeonameValue => GeonameVal - case OntologyConstants.KnoraApiV2Complex.ColorValue => ColorVal - case OntologyConstants.KnoraApiV2Complex.StillImageFileValue => StillImageFileVal - case OntologyConstants.KnoraApiV2Complex.MovingImageFileValue => MovingImageFileVal - case OntologyConstants.KnoraApiV2Complex.AudioFileValue => AudioFileVal - case OntologyConstants.KnoraApiV2Complex.DDDFileValue => DDDFileVal - case OntologyConstants.KnoraApiV2Complex.TextFileValue => TextFileVal - case OntologyConstants.KnoraApiV2Complex.DocumentFileValue => DocumentFileVal - case _ => throw ClientApiGenerationException(s"Unexpected value type: $ontologyObjectType") - } - } - - /** - * Given an object type that is not a Knora value type, returns the corresponding [[ClientObjectType]]. - * - * @param ontologyObjectType the RDF type. - * @return the corresponding [[ClientObjectType]]. - */ - private def nonResourcePropObjectTypeToClientObjectType(ontologyObjectType: SmartIri): ClientObjectType = { - if (ontologyObjectType.isClientCollectionTypeIri) { - ontologyObjectType.getClientCollectionType - } else { - ontologyObjectType.toString match { - case OntologyConstants.Xsd.String => StringDatatype - case OntologyConstants.Xsd.Boolean => BooleanDatatype - case OntologyConstants.Xsd.Integer => IntegerDatatype - case OntologyConstants.Xsd.Decimal => DecimalDatatype - case OntologyConstants.Xsd.Uri => UriDatatype - case OntologyConstants.Xsd.DateTime | OntologyConstants.Xsd.DateTimeStamp => DateTimeStampDatatype - - case _ => - ClassRef( - className = makeClientClassName(ontologyObjectType), - classIri = ontologyObjectType - ) - } - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/TypeScriptBackEnd.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/TypeScriptBackEnd.scala deleted file mode 100644 index 8e4a0b6d00..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/TypeScriptBackEnd.scala +++ /dev/null @@ -1,684 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import java.io.File -import java.nio.file.{Path, Paths} - -import org.knora.webapi.ClientApiGenerationException -import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality._ -import org.knora.webapi.util.clientapi.TypeScriptBackEnd.ImportInfo -import org.knora.webapi.util.{FileUtil, SmartIri, StringFormatter} - -/** - * Generates client API source code in TypeScript. - */ -class TypeScriptBackEnd extends GeneratorBackEnd { - private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - // The directory where TypeScript compilation stubs are located. - private val mockCodeDir: Path = Paths.get("_test_data/typescript-client-mock-src").toAbsolutePath - - /** - * Represents information about an endpoint and its source code. - * - * @param className the name of the endpoint class. - * @param description a description of the endpoint. - * @param variableName a variable name that can be used for an instance of the endpoint class. - * @param urlPath the URL path of the endpoint, relative to the API path. - * @param fileContent the endpoint's source code. - */ - private case class EndpointInfo(className: String, - description: String, - variableName: String, - urlPath: String, - fileContent: SourceCodeFileContent) { - /** - * Converts this [[EndpointInfo]] to an [[ImportInfo]] so the endpoint can be imported in another class. - * - * @param importedIn the path of the class in which the endpoint is to be imported. - * @param includeFileExtension if `true`, include the file extension in the import. - * @return an [[ImportInfo]] referring to this endpoint. - */ - def toImportInfo(importedIn: SourceCodeFilePath, includeFileExtension: Boolean = true): ImportInfo = { - val importPath: String = importedIn.makeImportPath(thatSourceCodeFilePath = fileContent.filePath, includeFileExtension = includeFileExtension) - - ImportInfo( - className = className, - importPath = importPath, - description = Some(description), - variableName = Some(variableName), - urlPath = Some(urlPath) - ) - } - } - - /** - * Generates client API source code. - * - * @param apis the APIs from which source code is to be generated. - * @return the generated source code. - */ - override def generateClientSourceCode(apis: Set[ClientApiBackendInput], params: Map[String, String]): Set[SourceCodeFileContent] = { - /** - * Returns the files containing mock knora-api-js-lib code, used for testing the generated code. - * - * @param startDir the directory to look for mock code in. - * @param fileContentAcc the mock code collected so far. - * @return the collected mock code. - */ - def getMockFiles(startDir: File, fileContentAcc: Set[SourceCodeFileContent]): Set[SourceCodeFileContent] = { - if (!startDir.exists) { - throw ClientApiGenerationException(s"Directory $startDir does not exist") - } - - if (!startDir.isDirectory) { - throw ClientApiGenerationException(s"Path $startDir does not represent a directory") - } - - val relativeStartPath: Seq[String] = mockCodeDir.relativize(startDir.toPath.toAbsolutePath).toString.split('/').toSeq.filterNot(_.isEmpty) - val fileList: Seq[File] = startDir.listFiles.toSeq.filterNot(_.getName.startsWith(".")) - val files: Seq[File] = fileList.filterNot(_.isDirectory) - val subdirectories: Seq[File] = fileList.filter(_.isDirectory) - - val fileContentInThisDir: Set[SourceCodeFileContent] = files.map { - file: File => - val filenameAndExtension: Array[String] = file.getName.split('.') - val filename = filenameAndExtension(0) - - val fileExtension = if (filenameAndExtension.length == 2) { - filenameAndExtension(1) - } else { - "" - } - - val sourceCodeFilePath = SourceCodeFilePath( - directoryPath = relativeStartPath, - filename = filename, - fileExtension = fileExtension - ) - - val text = FileUtil.readTextFile(file) - - SourceCodeFileContent( - filePath = sourceCodeFilePath, - text = text - ) - }.toSet - - val accForRecursion = fileContentAcc ++ fileContentInThisDir - - subdirectories.foldLeft(accForRecursion) { - case (acc, subdirectory) => - getMockFiles(startDir = subdirectory, fileContentAcc = acc) - } - } - - val knoraApiConnectionSourceCode: SourceCodeFileContent = generateKnoraApiConnectionSourceCode(apis.map(_.apiDef)) - - val mockCode: Set[SourceCodeFileContent] = if (params.contains("mock")) { - getMockFiles(startDir = mockCodeDir.toFile, fileContentAcc = Set.empty) - } else { - Set.empty - } - - apis.flatMap(api => generateApiSourceCode(api)) ++ mockCode + knoraApiConnectionSourceCode - } - - /** - * Generates TypeScript source code for an API. - * - * @param api the API for which source code is to be generated. - * @return a set of [[SourceCodeFileContent]] objects containing the generated source code. - */ - private def generateApiSourceCode(api: ClientApiBackendInput): Set[SourceCodeFileContent] = { - // Generate file paths for class definitions. - val clientClassCodePaths: Map[String, SourceCodeFilePath] = api.clientClassDefs.map { - clientClassDef => - val filePath: SourceCodeFilePath = makeClassFilePath(apiDef = api.apiDef, className = clientClassDef.className) - clientClassDef.className -> filePath - }.toMap - - // Generate source code for class definitions. - val classSourceCode: Set[SourceCodeFileContent] = if (api.apiDef.generateClasses) { - generateClassSourceCode( - apiDef = api.apiDef, - clientClassDefs = api.clientClassDefs, - clientClassCodePaths = clientClassCodePaths - ) - } else { - Set.empty - } - - // Generate source code for endpoints. - val endpointSourceCode: Seq[SourceCodeFileContent] = if (api.apiDef.generateEndpoints) { - val endpointInfos: Seq[EndpointInfo] = api.apiDef.endpoints.map { - endpoint => - generateEndpointInfo( - apiDef = api.apiDef, - endpoint = endpoint, - clientClassDefs = api.clientClassDefs, - clientClassCodePaths = clientClassCodePaths - ) - } - - // Generate source code for the main endpoint. - val mainEndpointSourceCode = generateMainEndpointSourceCode( - apiDef = api.apiDef, - endpointInfos = endpointInfos - ) - - endpointInfos.map(_.fileContent) :+ mainEndpointSourceCode - } else { - Seq.empty - } - - classSourceCode ++ endpointSourceCode - } - - /** - * Generates knora-api-connection.ts. - * - * @param apiDefs the API definitions to be used. - * @return the source code of knora-api-connection.ts. - */ - private def generateKnoraApiConnectionSourceCode(apiDefs: Set[ClientApi]): SourceCodeFileContent = { - val knoraApiConnectionFilePath = SourceCodeFilePath( - directoryPath = Seq.empty, - filename = "knora-api-connection", - fileExtension = "ts" - ) - - val importInfos: Set[ImportInfo] = apiDefs.filter(_.generateEndpoints).map { - apiDef => - val mainEndpointFilePath: SourceCodeFilePath = makeMainEndpointFilePath(apiDef) - - ImportInfo( - className = apiDef.name, - importPath = knoraApiConnectionFilePath.makeImportPath(mainEndpointFilePath, includeFileExtension = false), - description = Some(apiDef.description), - variableName = Some(makeVariableName(apiDef.name)), - urlPath = Some(apiDef.urlPath) - ) - } - - // Generate the source code of knora-api-connection.ts. - val text: String = clientapi.typescript.txt.generateKnoraApiConnection( - apis = importInfos.toVector.sortBy(_.className) - ).toString() - - SourceCodeFileContent( - filePath = SourceCodeFilePath( - directoryPath = Seq.empty, - filename = "knora-api-connection", - fileExtension = "ts" - ), - text = text - ) - } - - /** - * Generates source code for the main endpoint of an API. - * - * @param apiDef the API definition. - * @param endpointInfos information about the endpoints that belong to the API. - * @return the source code for the main endpoint. - */ - private def generateMainEndpointSourceCode(apiDef: ClientApi, - endpointInfos: Seq[EndpointInfo]): SourceCodeFileContent = { - // Generate the main endpoint's file path. - val mainEndpointFilePath: SourceCodeFilePath = makeMainEndpointFilePath(apiDef) - - // Generate the main endpoint's source code. - val text: String = clientapi.typescript.txt.generateTypeScriptMainEndpoint( - name = apiDef.name, - description = apiDef.description, - endpoints = endpointInfos.map(_.toImportInfo(importedIn = mainEndpointFilePath, includeFileExtension = false)) - ).toString() - - SourceCodeFileContent(filePath = mainEndpointFilePath, text = text) - } - - /** - * Generates source code for an API endpoint. - * - * @param apiDef the API definition. - * @param endpoint the endpoint definition. - * @param clientClassDefs the definitions of the classes used in the API. - * @param clientClassCodePaths the file paths of generated class definitions. - * @return the source code of the endpoint. - */ - private def generateEndpointInfo(apiDef: ClientApi, - endpoint: ClientEndpoint, - clientClassDefs: Set[ClientClassDefinition], - clientClassCodePaths: Map[String, SourceCodeFilePath]): EndpointInfo = { - // Generate the endpoint's file path. - val endpointFilePath: SourceCodeFilePath = makeEndpointFilePath(apiDef = apiDef, endpoint = endpoint) - - // Determine which classes need to be imported by the endpoint. - val classDefsImported: Set[ClientClassDefinition] = clientClassDefs.filter { - clientClassDef => endpoint.classIrisUsed.contains(clientClassDef.classIri) - } - - // Make an ImportInfo for each imported class. - val classInfos: Vector[ImportInfo] = classDefsImported.toVector.sortBy(_.classIri).map { - clientClassDef => - ImportInfo( - className = clientClassDef.className, - description = clientClassDef.classDescription, - importPath = endpointFilePath.makeImportPath(clientClassCodePaths(clientClassDef.className), includeFileExtension = false) - ) - } - - // Generate the source code of the endpoint. - val text: String = clientapi.typescript.txt.generateTypeScriptEndpoint( - name = endpoint.name, - description = endpoint.description, - importedClasses = classInfos, - functions = endpoint.functions - ).toString() - - val fileContent = SourceCodeFileContent(filePath = endpointFilePath, text = text) - - EndpointInfo( - className = endpoint.name, - description = endpoint.description, - variableName = makeVariableName(endpoint.name), - urlPath = endpoint.urlPath, - fileContent = fileContent - ) - } - - /** - * Generates source code for classes. - * - * @param apiDef the API definition. - * @param clientClassDefs the definitions of the classes for which source code is to be generated. - * @param clientClassCodePaths the file paths to be used for the generated classes. - * @return the generated source code. - */ - private def generateClassSourceCode(apiDef: ClientApi, - clientClassDefs: Set[ClientClassDefinition], - clientClassCodePaths: Map[String, SourceCodeFilePath]): Set[SourceCodeFileContent] = { - clientClassDefs.map { - clientClassDef => - val classFilePath: SourceCodeFilePath = clientClassCodePaths(clientClassDef.className) - - val subClassOf: Option[ClassRef] = clientClassDef.subClassOf.map { - subClassOfIri: SmartIri => ClassRef(className = subClassOfIri.getEntityName.capitalize, classIri = subClassOfIri) - } - - val propertiesNeedingCustomConverters: Vector[ClientPropertyDefinition] = clientClassDef.properties.filter { - propertyDef => propertyDef.objectType match { - case _: CollectionType => true - case _ => false - } - } - - val customConverterImports: Vector[ImportInfo] = propertiesNeedingCustomConverters.map { - propertyDef => - val converterName: String = TypeScriptBackEnd.makeConverterName(propertyDef) - - val converterPath: SourceCodeFilePath = makeCustomConverterFilePath( - apiDef = apiDef, - propertyDefinition = propertyDef, - converterClassName = converterName - ) - - val convertImportPath = classFilePath.makeImportPath( - thatSourceCodeFilePath = converterPath, - includeFileExtension = false - ) - - ImportInfo( - className = converterName, - importPath = convertImportPath - ) - } - - val importsWithBaseClass: Set[ClassRef] = clientClassDef.classObjectTypesUsed ++ subClassOf - - val importInfos: Vector[ImportInfo] = importsWithBaseClass.toVector.sortBy(_.classIri).map { - classRef => - val classImportPath: String = classFilePath.makeImportPath( - thatSourceCodeFilePath = clientClassCodePaths(classRef.className), - includeFileExtension = false - ) - - ImportInfo( - className = classRef.className, - importPath = classImportPath - ) - } ++ customConverterImports - - val classText: String = clientapi.typescript.txt.generateTypeScriptClass( - classDef = clientClassDef, - subClassOf = subClassOf, - importedClasses = importInfos - ).toString() - - SourceCodeFileContent(filePath = classFilePath, text = classText) - } - } - - /** - * Generates the file path of an API's main endpoint. - * - * @param apiDef the API definition. - * @return the file path of the API's main endpoint. - */ - private def makeMainEndpointFilePath(apiDef: ClientApi): SourceCodeFilePath = { - val apiLocalName: String = stringFormatter.camelCaseToSeparatedLowerCase(apiDef.name) - - SourceCodeFilePath( - directoryPath = Seq("api", apiDef.directoryName), - filename = s"$apiLocalName", - fileExtension = "ts" - ) - } - - /** - * Generates the file path of an endpoint. - * - * @param apiDef the API definition. - * @param endpoint the definition of the endpoint. - * @return the file path of the endpoint. - */ - private def makeEndpointFilePath(apiDef: ClientApi, endpoint: ClientEndpoint): SourceCodeFilePath = { - val endpointLocalName: String = stringFormatter.camelCaseToSeparatedLowerCase(endpoint.name) - - SourceCodeFilePath( - directoryPath = Seq("api", apiDef.directoryName, endpoint.directoryName), - filename = endpointLocalName, - fileExtension = "ts" - ) - } - - /** - * Generates the file path of a class. - * - * @param apiDef the API definition. - * @param className the name of the class. - * @return the file path of the generated class. - */ - private def makeClassFilePath(apiDef: ClientApi, className: String): SourceCodeFilePath = { - val classLocalName: String = stringFormatter.camelCaseToSeparatedLowerCase(className) - - SourceCodeFilePath( - directoryPath = Seq("models", apiDef.directoryName), - filename = classLocalName, - fileExtension = "ts" - ) - } - - /** - * Generates the file path of a custom converter that is expected to exist. - * - * @param apiDef the API definition. - * @param propertyDefinition the definition of the property that needs a custom converter. - * @return the file path of the custom converter. - */ - private def makeCustomConverterFilePath(apiDef: ClientApi, - propertyDefinition: ClientPropertyDefinition, - converterClassName: String): SourceCodeFilePath = { - val converterLocalName: String = stringFormatter.camelCaseToSeparatedLowerCase(converterClassName) - - SourceCodeFilePath( - directoryPath = Seq("models", apiDef.directoryName, "custom-converters"), - filename = converterLocalName, - fileExtension = "ts" - ) - } - - /** - * Generates a variable name that can be used for an instance of a class. - * - * @param className the name of the class. - * @return a variable name that can be used for an instance of the class. - */ - private def makeVariableName(className: String): String = { - className.substring(0, 1).toLowerCase + className.substring(1) - } -} - -/** - * Classes and functions used by Twirl templates. - */ -object TypeScriptBackEnd { - - /** - * Represents information about an imported class or interface. - * - * @param className the name of the class. - * @param importPath the file path to be used for importing the class. - * @param description a description of the class. - * @param variableName a variable name that can be used for an instance of the class. - * @param urlPath if this class represents an endpoint, the URL path of the endpoint. - */ - case class ImportInfo(className: String, - importPath: String, - description: Option[String] = None, - variableName: Option[String] = None, - urlPath: Option[String] = None) - - /** - * Generates the name of a custom converter for a property whose object type is a collection. - * - * @param propertyDefinition the property definition. - * @return the name of the custom converter. - */ - def makeConverterName(propertyDefinition: ClientPropertyDefinition): String = { - s"${propertyDefinition.propertyName.capitalize}Converter" - } - - /** - * Generates the type name used for a property's object type in json2typescript's `@JsonProperty` annotation. - * - * @param propertyDefinition the property definition. - * @return the type name. - */ - def makeJson2TypeScriptType(propertyDefinition: ClientPropertyDefinition): String = { - propertyDefinition.objectType match { - case _: CollectionType => - // json2typescript can't handle arbitrary collection signatures, so we assume there's a custom - // converter for each collection type. - makeConverterName(propertyDefinition) - - case other => - val propertyObjectType: String = makePropertyObjectType(other) - - // Capitalise TypeScript datatype names. (Class references are already capitalised.) - val capitalisedType = propertyDefinition.objectType match { - case _: ClientDatatype => propertyObjectType.capitalize - case _ => propertyObjectType - } - - // If the property can have multiple values, wrap the type in brackets. - if (propertyDefinition.cardinality == MayHaveMany || propertyDefinition.cardinality == MustHaveSome) { - s"[$capitalisedType]" - } else { - capitalisedType - } - } - } - - /** - * Generates a type annotation for a property object type. - * - * @param objectType the object type. - * @return the corresponding type annotation. - */ - def makePropertyObjectType(objectType: ClientObjectType): String = { - objectType match { - case StringDatatype => "string" - case BooleanDatatype => "boolean" - case IntegerDatatype => "number" - case DecimalDatatype => "number" - case UriDatatype => "string" - case DateTimeStampDatatype => "string" - case classRef: ClassRef => classRef.className - case enum: EnumDatatype => enum.values.map(value => "\"" + value + "\"").mkString(" | ") - case arrayType: ArrayType => s"${makePropertyObjectType(arrayType.elementType)}[]" - case mapType: MapType => s"{ [key: ${makePropertyObjectType(mapType.keyType)}]: ${makePropertyObjectType(mapType.valueType)} }" - case _ => throw ClientApiGenerationException(s"Type $objectType not yet supported") - } - } - - /** - * Generates the default value for a property object type. - * - * @param propertyDefinition the property definition. - * @return the default value. - */ - def makeDefaultValue(propertyDefinition: ClientPropertyDefinition): String = { - propertyDefinition.cardinality match { - case MayHaveMany => - if (propertyDefinition.isOptionalSet) { - "undefined" - } else { - "[]" - } - - case MustHaveSome => "[]" - - case MayHaveOne => "undefined" - - case MustHaveOne => - propertyDefinition.objectType match { - case StringDatatype => "\"\"" - case BooleanDatatype => "false" - case IntegerDatatype => "0" - case DecimalDatatype => "0" - case UriDatatype => "\"\"" - case DateTimeStampDatatype => "\"\"" - case classRef: ClassRef => s"new ${classRef.className}()" - case _: ArrayType => "[]" - case _: MapType => "{}" - case other => throw ClientApiGenerationException(s"Type $other not supported in this template") - } - } - } - - /** - * Adds `[]` to the end of a type if the cardinality allows it to be an array. - * - * @param propertyDefinition the property definition. - * @return the type, followed by `[]` if necessary. - */ - def typeWithBracketsForCardinality(propertyDefinition: ClientPropertyDefinition): String = { - val typeScriptType: String = makePropertyObjectType(propertyDefinition.objectType) - - if (propertyDefinition.cardinality == MayHaveMany || propertyDefinition.cardinality == MustHaveSome) { - s"$typeScriptType[]" - } else { - typeScriptType - } - } - - /** - * Returns `?` if a property's cardinality represents an optional value. - * - * @param propertyDefinition the property definition. - * @return `?` if the cardinality represents an optional value, otherwise the empty string. - */ - def handleOptionalProperty(propertyDefinition: ClientPropertyDefinition): String = { - if (propertyDefinition.cardinality == MayHaveOne || propertyDefinition.isOptionalSet) { - "?" - } else { - "" - } - } - - /** - * Generates the name of the function for the specified HTTP method. - * - * @param httpMethod the HTTP method. - * @return the name of the corresponding function. - */ - def makeHttpMethod(httpMethod: ClientHttpMethod): String = { - httpMethod match { - case GET => "httpGet" - case POST => "httpPost" - case PUT => "httpPut" - case DELETE => "httpDelete" - } - } - - /** - * Generates the TypeScript representation of a value (a literal or variable) used in a function. - * - * @param value the value. - * @return the TypeScript representation of the value. - */ - def makeValue(value: Value): String = { - value match { - case stringLiteral: StringLiteralValue => "\"" + stringLiteral + "\"" - case booleanLiteral: BooleanLiteralValue => "\"" + booleanLiteral + "\"" - case integerLiteral: IntegerLiteralValue => integerLiteral.toString - case arg: ArgValue => arg.name + arg.memberVariableName.map(varName => "." + varName).getOrElse("") - } - } - - /** - * Generates the TypeScript representation of the URL of a Knora route. - * - * @param urlElementsAsValues the URL elements represented as a sequence of [[Value]] objects. - * @return the TypeScript representation of the URL. - */ - def makeUrl(urlElementsAsValues: Seq[Value]): String = { - if (urlElementsAsValues.isEmpty) { - "\"\"" - } else { - urlElementsAsValues.map { - value: Value => - value match { - case arg: ArgValue => "encodeURIComponent(" + makeValue(arg) + ")" - case _ => makeValue(value) - } - }.mkString(" + ") - } - } - - /** - * Generates the TypeScript representation of the body of an HTTP request. - * - * @param maybeRequestBody the request body, or `None` if there is none. - * @return the TypeScript representation of the HTTP request. - */ - def makeHttpRequestBody(maybeRequestBody: Option[HttpRequestBody]): String = { - maybeRequestBody match { - case Some(requestBody) => - val requestBodyContent = requestBody match { - case jsonRequestBody: JsonRequestBody => - "{ " + - jsonRequestBody.jsonObject.map { - case (key, value) => key + ": " + makeValue(value) - }.mkString(", ") + - " }" - - case arg: ArgValue => s"this.jsonConvert.serializeObject(${makeValue(arg)})" - } - - ", " + requestBodyContent - - case None => "" - } - } -} diff --git a/webapi/src/main/twirl/clientapi/typescript/generateKnoraApiConnection.scala.txt b/webapi/src/main/twirl/clientapi/typescript/generateKnoraApiConnection.scala.txt deleted file mode 100644 index 4d7897c8d6..0000000000 --- a/webapi/src/main/twirl/clientapi/typescript/generateKnoraApiConnection.scala.txt +++ /dev/null @@ -1,53 +0,0 @@ -@* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - *@ - -@import org.knora.webapi._ -@import org.knora.webapi.util._ -@import org.knora.webapi.util.clientapi._ -@import org.knora.webapi.util.clientapi.TypeScriptBackEnd.ImportInfo - -@(apis: Seq[ImportInfo])import { KnoraApiConfig } from "./knora-api-config"; - -@for(api <- apis) {import { @api.className } from "@api.importPath"; -} -/** - * Offers methods for JavaScript developers to interact with the Knora API. - */ -export class KnoraApiConnection { - @for(api <- apis) { - /** - * @api.description.get - */ - readonly @api.variableName: @api.className; - } - /** - * Constructor. - * Sets up all endpoints for the Knora API. - * - * @@param knoraApiConfig - */ - constructor(knoraApiConfig: KnoraApiConfig) { - - // Instantiate the endpoints - @for(api <- apis) { - this.@api.variableName.get = new @{api.className}(knoraApiConfig, "@api.urlPath.get"); - } - - } -} diff --git a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptClass.scala.txt b/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptClass.scala.txt deleted file mode 100644 index 9b7432ddeb..0000000000 --- a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptClass.scala.txt +++ /dev/null @@ -1,57 +0,0 @@ -@* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - *@ - -@import org.knora.webapi._ -@import org.knora.webapi.util._ -@import org.knora.webapi.util.clientapi._ -@import org.knora.webapi.util.clientapi.TypeScriptBackEnd._ -@import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality._ - -@(classDef: ClientClassDefinition, - subClassOf: Option[ClassRef], - importedClasses: Seq[ImportInfo])import { JsonObject, JsonProperty } from "json2typescript"; - -@for(classInfo <- importedClasses) {import { @classInfo.className } from "@classInfo.importPath"; -}@classDef.classDescription match { - case Some(classDescStr) => { -/** - * @classDescStr - */} - - case None => {} -} -@@JsonObject("@{classDef.className}") -export class @{classDef.className}@{ - subClassOf match { - case Some(subClassDef) => " extends " + subClassDef.className - case None => "" - } -} { -@for(property <- classDef.properties) { -@property.propertyDescription match { - case Some(propDescStr) => { /** - * @propDescStr - */} - - case None => {} - } - @@JsonProperty("@{property.propertyName}", @makeJson2TypeScriptType(property)@if(property.cardinality == MayHaveOne || property.isOptionalSet) {, true}) - @{property.propertyName}@handleOptionalProperty(property): @typeWithBracketsForCardinality(property) = @makeDefaultValue(property); -} -} \ No newline at end of file diff --git a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptEndpoint.scala.txt b/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptEndpoint.scala.txt deleted file mode 100644 index 116f0cd4fb..0000000000 --- a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptEndpoint.scala.txt +++ /dev/null @@ -1,62 +0,0 @@ -@* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - *@ - -@import org.knora.webapi._ -@import org.knora.webapi.util._ -@import org.knora.webapi.util.clientapi._ -@import org.knora.webapi.util.clientapi.TypeScriptBackEnd._ - -@(name: String, - description: String, - importedClasses: Seq[ImportInfo], - functions: Seq[ClientFunction])import { Observable } from "rxjs"; -import { catchError, map } from "rxjs/operators"; - -import { ApiResponseData } from "../../../models/api-response-data"; -import { ApiResponseError } from "../../../models/api-response-error"; -import { Endpoint } from "../../endpoint"; - -@for(classInfo <- importedClasses) {import { @classInfo.className } from "@classInfo.importPath"; -} - -/** - * @description - */ -export class @name extends Endpoint { - @for(function <- functions) { - /** - * @function.description@if(function.params.nonEmpty) { - * @for(param <- function.params) { - * @@param @param.name @param.description}} - */ - @{function.name}(@for((param, index) <- function.params.zipWithIndex) {@if(index > 0) {, }@param.name: @makePropertyObjectType(param.objectType)}): Observable | ApiResponseError> { - @function.implementation match { - case httpRequest: ClientHttpRequest => { - return this.@{makeHttpMethod(httpRequest.httpMethod)}(@makeUrl(httpRequest.urlElementsAsValues)@makeHttpRequestBody(httpRequest.requestBody)).pipe( - map(ajaxResponse => ApiResponseData.fromAjaxResponse(ajaxResponse, @makePropertyObjectType(function.returnType), this.jsonConvert)), - catchError(error => this.handleError(error)) - ); - } - - case functionCall: FunctionCall => { - return this.@{functionCall.name}(@{functionCall.args.map(arg => makeValue(arg)).mkString(", ")}); - }} - } - } -} \ No newline at end of file diff --git a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptMainEndpoint.scala.txt b/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptMainEndpoint.scala.txt deleted file mode 100644 index 18a82b5965..0000000000 --- a/webapi/src/main/twirl/clientapi/typescript/generateTypeScriptMainEndpoint.scala.txt +++ /dev/null @@ -1,59 +0,0 @@ -@* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - *@ - -@import org.knora.webapi._ -@import org.knora.webapi.util._ -@import org.knora.webapi.util.clientapi._ -@import org.knora.webapi.util.clientapi.TypeScriptBackEnd.ImportInfo - -@(name: String, - description: String, - endpoints: Seq[ImportInfo])import { KnoraApiConfig } from "../../knora-api-config"; -import { Endpoint } from "../endpoint"; - -@for(endpoint <- endpoints) {import { @endpoint.className } from "@endpoint.importPath"; -} - -/** - * @description - */ -export class @name extends Endpoint { - @for(endpoint <- endpoints) { - /** - * @endpoint.description.get - */ - readonly @endpoint.variableName.get: @endpoint.className; - } - - /** - * Constructor. - * Sets up all endpoints for this endpoint. - * - * @@param knoraApiConfig - * @@param path - */ - constructor(protected readonly knoraApiConfig: KnoraApiConfig, protected readonly path: string) { - - super(knoraApiConfig, path); - - // Instantiate the endpoints - @for(endpoint <- endpoints) { - this.@endpoint.variableName.get = new @{endpoint.className}(knoraApiConfig, path + "@endpoint.urlPath.get");} - } -} diff --git a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld index 6585bd00b8..85200870ea 100644 --- a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld +++ b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld @@ -5467,7 +5467,7 @@ "@id" : "knora-api:attachedToProject", "@type" : "owl:ObjectProperty", "knora-api:objectType" : { - "@id" : "knora-admin:Project" + "@id" : "xsd:anyURI" }, "rdfs:comment" : "Connects something to a project", "rdfs:label" : "attached to project" @@ -5475,7 +5475,7 @@ "@id" : "knora-api:attachedToUser", "@type" : "owl:ObjectProperty", "knora-api:objectType" : { - "@id" : "knora-admin:User" + "@id" : "xsd:anyURI" }, "rdfs:comment" : "Connects something to a user", "rdfs:label" : "attached to user" @@ -5708,7 +5708,7 @@ "@id" : "knora-api:deletedBy", "@type" : "owl:ObjectProperty", "knora-api:objectType" : { - "@id" : "knora-admin:User" + "@id" : "xsd:anyURI" }, "rdfs:comment" : "Indicates who deleted a resource or value" }, { @@ -6980,7 +6980,6 @@ "@context" : { "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - "knora-admin" : "http://api.knora.org/ontology/knora-admin/v2#", "owl" : "http://www.w3.org/2002/07/owl#", "salsah-gui" : "http://api.knora.org/ontology/salsah-gui/v2#", "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", diff --git a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.rdf b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.rdf index 225d890a92..7da0eecde7 100644 --- a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.rdf +++ b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.rdf @@ -2,7 +2,6 @@ A generic class for representing annotations Annotation - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + true Represents something in the world, or an abstract thing Resource - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + true 1 @@ -72,29 +71,29 @@ - + true 1 - + Connects something to a project attached to project - + true 1 - + Connects something to a user attached to user - + true 1 @@ -105,7 +104,7 @@ - + true 1 @@ -115,7 +114,7 @@ - + true 1 @@ -125,17 +124,17 @@ - + true 1 - + Indicates who deleted a resource or value - + 1 @@ -150,7 +149,7 @@ - + true 0 @@ -165,7 +164,7 @@ - + true 1 @@ -174,7 +173,7 @@ - + true 0 @@ -189,7 +188,7 @@ - + true 0 @@ -204,7 +203,7 @@ - + 1 @@ -218,7 +217,7 @@ - + 1 @@ -231,7 +230,7 @@ - + true 1 @@ -241,7 +240,7 @@ - + true 1 @@ -250,7 +249,7 @@ - + true 1 @@ -261,7 +260,7 @@ - + true 1 @@ -272,7 +271,7 @@ - + true 1 @@ -283,7 +282,7 @@ - + true 1 @@ -294,53 +293,53 @@ true Represents an audio file - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + true - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + true 1 - + true 1 - + 1 @@ -352,22 +351,22 @@ - + true 1 - + true 1 - + true 1 - + true 1 @@ -380,7 +379,7 @@ - + true 1 @@ -393,22 +392,22 @@ - + true 1 - + true 1 - + true 1 - + true 1 @@ -419,7 +418,7 @@ - + true 1 @@ -430,7 +429,7 @@ - + true 1 @@ -442,7 +441,7 @@ - + true 1 @@ -454,7 +453,7 @@ - + true 1 @@ -464,85 +463,85 @@ Represents a file containing audio data Representation (Audio) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + true A resource that can store a file Representation - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -557,62 +556,62 @@ - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + - + 1 @@ -629,105 +628,105 @@ Represents a boolean value - - - - - - - - - - - - - - + + + + + + + + + + + + + + true The base class of classes representing Knora values - - - - - - - - - - - - - + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -735,7 +734,7 @@ - + 1 @@ -754,87 +753,87 @@ Represents a color in HTML format, e.g. "#33eeff" - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -843,93 +842,93 @@ true This represents some 3D-object with mesh data, point cloud, etc. - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -939,61 +938,61 @@ Represents a file containg 3D data Representation (3D) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -1008,69 +1007,69 @@ - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - - - - - - - - - + + + + + + + + + - + 1 @@ -1082,7 +1081,7 @@ - + 1 @@ -1094,7 +1093,7 @@ - + 1 @@ -1106,7 +1105,7 @@ - + 1 @@ -1118,7 +1117,7 @@ - + 1 @@ -1130,7 +1129,7 @@ - + 1 @@ -1142,7 +1141,7 @@ - + 1 @@ -1154,7 +1153,7 @@ - + 1 @@ -1166,7 +1165,7 @@ - + 1 @@ -1183,135 +1182,135 @@ Represents a Knora date value - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1319,7 +1318,7 @@ - + 1 @@ -1338,87 +1337,87 @@ Represents an arbitrary-precision decimal value - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1426,51 +1425,51 @@ true - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -1482,7 +1481,7 @@ - + 1 @@ -1494,7 +1493,7 @@ - + 1 @@ -1506,52 +1505,52 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1560,61 +1559,61 @@ true Representation (Document) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -1629,125 +1628,125 @@ - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 - + 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1757,110 +1756,110 @@ A ForbiddenResource is a proxy for a resource that the client has insufficient permissions to see. A ForbiddenResource is a proxy for a resource that the client has insufficient permissions to see. - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 0 - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1869,47 +1868,47 @@ true Represents a geometrical objects as JSON string - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -1921,42 +1920,42 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -1964,47 +1963,47 @@ true - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -2016,42 +2015,42 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2059,7 +2058,7 @@ - + 1 @@ -2078,97 +2077,97 @@ Represents an integer value - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - - + + - + 1 @@ -2180,7 +2179,7 @@ - + 1 @@ -2197,93 +2196,93 @@ Represents a time interval, e.g. in an audio recording - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2295,72 +2294,72 @@ Represents a generic link object Link Object - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 0 - + true 0 - + 1 @@ -2375,7 +2374,7 @@ - + 1 @@ -2390,47 +2389,47 @@ - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2440,60 +2439,60 @@ A reification node that describes direct links between resources - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -2505,7 +2504,7 @@ - + 1 @@ -2517,7 +2516,7 @@ - + 1 @@ -2529,7 +2528,7 @@ - + 1 @@ -2541,103 +2540,103 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 Represents a flat or hierarchical list - - + + - + 1 - + 1 true - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -2649,32 +2648,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2683,72 +2682,72 @@ true Represents a moving image file - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -2760,7 +2759,7 @@ - + 1 @@ -2772,7 +2771,7 @@ - + 1 @@ -2784,7 +2783,7 @@ - + 1 @@ -2796,32 +2795,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2831,66 +2830,66 @@ A resource containing moving image data Representation (Movie) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 0 - + 1 @@ -2905,47 +2904,47 @@ - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -2957,65 +2956,65 @@ Represents a geometric region of a resource. The geometry is represented currently as JSON string. Region - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -3031,11 +3030,11 @@ - + 1 - + 1 @@ -3050,32 +3049,32 @@ - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + 1 @@ -3090,7 +3089,7 @@ - + 1 @@ -3105,67 +3104,67 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -3179,121 +3178,121 @@ - + true 0 - + true 1 - + true 0 - + true 0 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 0 - + 1 - + 0 - + 0 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 @@ -3302,39 +3301,39 @@ Represents a boolean in a TextValue - - - - - - - - - - - + + + + + + + + + + + true Represents a knora-base value type in a TextValue - - - - - - - - - - + + + + + + + + + + - + true 1 - + true 1 @@ -3344,7 +3343,7 @@ - + true 1 @@ -3354,7 +3353,7 @@ - + true 1 @@ -3364,7 +3363,7 @@ - + true 1 @@ -3376,7 +3375,7 @@ - + true 1 @@ -3386,7 +3385,7 @@ - + true 1 @@ -3396,7 +3395,7 @@ - + true 1 @@ -3406,7 +3405,7 @@ - + true 1 @@ -3417,7 +3416,7 @@ - + true 1 @@ -3429,7 +3428,7 @@ - + true 1 @@ -3444,69 +3443,69 @@ Represents a color in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3514,63 +3513,63 @@ true Represents a standoff markup tag - - - - - - - - - - + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3580,117 +3579,117 @@ Represents a date in a TextValue - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3700,69 +3699,69 @@ Represents a decimal (floating point) value in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3772,69 +3771,69 @@ Represents an integer value in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3844,39 +3843,39 @@ Represents an internal reference in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -3885,32 +3884,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3920,75 +3919,75 @@ Represents an interval in a TextValue - - - - - - - - - - - - + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -3997,39 +3996,39 @@ true Represents a reference to a Knora resource in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -4038,73 +4037,73 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 @@ -4113,73 +4112,73 @@ Represents a timestamp in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4195,73 +4194,73 @@ Represents an arbitrary URI in a TextValue - - - - - - - - - - - + + + + + + + + + + + - + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4278,71 +4277,71 @@ true A file containing a two-dimensional still image - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -4354,7 +4353,7 @@ - + 1 @@ -4366,7 +4365,7 @@ - + 1 @@ -4378,32 +4377,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4413,81 +4412,81 @@ A resource that can contain a two-dimensional still image file Representation (Image) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 0 - + true 1 - + true 0 - + true 0 - + 1 @@ -4502,32 +4501,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4536,93 +4535,93 @@ true A text file such as plain Unicode text, LaTeX, TEI/XML, etc. - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4632,81 +4631,81 @@ A resource containing a text file Representation (Text) - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 0 - + true 1 - + true 0 - + true 0 - + 1 @@ -4721,32 +4720,32 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 @@ -4754,63 +4753,63 @@ true - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -4822,7 +4821,7 @@ - + 1 @@ -4834,7 +4833,7 @@ - + 1 @@ -4846,7 +4845,7 @@ - + 1 @@ -4858,7 +4857,7 @@ - + 1 @@ -4870,7 +4869,7 @@ - + 1 @@ -4882,7 +4881,7 @@ - + 0 @@ -4894,37 +4893,37 @@ - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -4933,92 +4932,92 @@ Represents a timestamp - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 @@ -5027,140 +5026,140 @@ Represents a URI - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 - + 1 @@ -5169,110 +5168,110 @@ a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML. a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML. - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 0 - + true 1 - + true 0 - + true 0 - + 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 - + true 1 diff --git a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl index 8620c3e216..c912e30620 100644 --- a/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl +++ b/webapi/src/test/resources/test-data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl @@ -1,4 +1,3 @@ -@prefix knora-admin: . @prefix knora-api: . @prefix owl: . @prefix rdf: . @@ -158,12 +157,12 @@ knora-api:arkUrl a owl:DatatypeProperty; rdfs:label "ARK URL" . knora-api:attachedToProject a owl:ObjectProperty; - knora-api:objectType knora-admin:Project; + knora-api:objectType xsd:anyURI; rdfs:comment "Connects something to a project"; rdfs:label "attached to project" . knora-api:attachedToUser a owl:ObjectProperty; - knora-api:objectType knora-admin:User; + knora-api:objectType xsd:anyURI; rdfs:comment "Connects something to a user"; rdfs:label "attached to user" . @@ -181,7 +180,7 @@ knora-api:deleteDate a owl:DatatypeProperty; rdfs:comment "Indicates when a resource or value was deleted" . knora-api:deletedBy a owl:ObjectProperty; - knora-api:objectType knora-admin:User; + knora-api:objectType xsd:anyURI; rdfs:comment "Indicates who deleted a resource or value" . knora-api:hasComment a owl:ObjectProperty; diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/ClientApiRouteE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/ClientApiRouteE2ESpec.scala index 162e482d61..7b3ecd6d73 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/ClientApiRouteE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/ClientApiRouteE2ESpec.scala @@ -19,20 +19,16 @@ package org.knora.webapi.e2e -import java.nio.file.{Files, Path} - import akka.actor.ActorSystem import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.testkit.RouteTestTimeout import com.typesafe.config.{Config, ConfigFactory} -import org.apache.commons.io.FileUtils import org.knora.webapi.E2ESpec import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.testing.tags.E2ETest -import org.knora.webapi.util.FileUtil +import org.knora.webapi.util.MessageUtil import scala.concurrent.duration._ -import scala.sys.process._ object ClientApiRouteE2ESpec { val config: Config = ConfigFactory.parseString( @@ -50,14 +46,13 @@ class ClientApiRouteE2ESpec extends E2ESpec(ClientApiRouteE2ESpec.config) { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "_test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), RdfDataObject(path = "_test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything"), RdfDataObject(path = "_test_data/ontologies/minimal-onto.ttl", name = "http://www.knora.org/ontology/0001/minimal") ) "The client API route" should { - "generate a Zip file of TypeScript code that compiles" in { - val request = Get(baseApiUrl + s"/clientapi/typescript?mock=true") + "generate a Zip file of client test data" in { + val request = Get(baseApiUrl + s"/clientapitest") val response: HttpResponse = singleAwaitingRequest(request = request, duration = 40960.millis) val responseBytes: Array[Byte] = getResponseEntityBytes(response) val filenames: Set[String] = getZipContents(responseBytes) @@ -65,40 +60,24 @@ class ClientApiRouteE2ESpec extends E2ESpec(ClientApiRouteE2ESpec.config) { // Check that some expected filenames are included in the Zip file. val expectedFilenames: Set[String] = Set( - "./package.json", - "./tsconfig.json", - "./knora-api-config.ts", - "./knora-api-connection.ts", - "./api/endpoint.ts", - "./api/admin/admin-endpoint.ts", - "./api/admin/users/users-endpoint.ts", - "./api/admin/groups/groups-endpoint.ts", - "./api/admin/projects/projects-endpoint.ts", - "./api/admin/permissions/permissions-endpoint.ts", - "./api/admin/lists/lists-endpoint.ts", - "./models/admin/user.ts" + "test-data/admin/groups/create-group-request.json", + "test-data/admin/groups/get-group-response.json", + "test-data/admin/lists/create-list-request.json", + "test-data/admin/lists/get-list-response.json", + "test-data/v2/lists/treelist.json", + "test-data/v2/ontologies/knora-api-ontology.json", + "test-data/v2/resources/create-resource-as-user.json", + "test-data/v2/resources/resource-graph.json", + "test-data/v2/resources/resource-preview.json", + "test-data/v2/resources/testding.json", + "test-data/v2/resources/thing-with-picture.json", + "test-data/v2/search/things.json", + "test-data/v2/search/thing-links.json", + "test-data/v2/values/create-boolean-value-request.json", + "test-data/v2/values/get-boolean-value-response.json" ) assert(expectedFilenames.subsetOf(filenames)) - - // Unzip the file to a temporary directory. - val tempDir: Path = Files.createTempDirectory("clientapi") - val zipFilePath: Path = tempDir.resolve("clientapi.zip") - val srcPath: Path = tempDir.resolve("src") - srcPath.toFile.mkdir() - FileUtil.writeBinaryFile(zipFilePath.toFile, responseBytes) - unzip(zipFilePath = zipFilePath, outputPath = srcPath) - - // Run 'npm install'. - val npmInstallExitCode: Int = Process(Seq("npm", "install"), srcPath.toFile).! - assert(npmInstallExitCode == 0) - - // Run the TypeScript compiler. - val typeScriptCompilerExitCode: Int = Process(Seq("./node_modules/typescript/bin/tsc"), srcPath.toFile).! - assert(typeScriptCompilerExitCode == 0) - - // Delete the temporary directory. - FileUtils.deleteDirectory(tempDir.toFile) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala index f3739b5485..794ee1464b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala @@ -25,17 +25,15 @@ import com.typesafe.scalalogging.LazyLogging import org.knora.webapi._ import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality._ import org.knora.webapi.messages.v2.responder.ontologymessages._ -import org.knora.webapi.routing.admin.AdminClientApi import org.knora.webapi.util.IriConversions._ import org.knora.webapi.util.jsonld._ import org.knora.webapi.util.{OntologyUtil, SmartIri, StringFormatter} -import spray.json._ import scala.collection.mutable /** - * A factory that constructs [[InstanceChecker]] instances for different Knora response formats. - */ + * A factory that constructs [[InstanceChecker]] instances for different Knora response formats. + */ object InstanceChecker { // A cache of class definitions. private val classDefCache: mutable.Map[SmartIri, ClassInfoContentV2] = mutable.Map.empty @@ -44,33 +42,28 @@ object InstanceChecker { private val propertyDefCache: mutable.Map[SmartIri, PropertyInfoContentV2] = mutable.Map.empty /** - * Returns an [[InstanceChecker]] for Knora responses in JSON format. - */ - def getJsonChecker: InstanceChecker = new InstanceChecker(new JsonInstanceInspector) - - /** - * Returns an [[InstanceChecker]] for Knora responses in JSON-LD format. - */ + * Returns an [[InstanceChecker]] for Knora responses in JSON-LD format. + */ def getJsonLDChecker: InstanceChecker = new InstanceChecker(new JsonLDInstanceInspector) } /** - * A test utility for checking whether an instance of an RDF class returned in a Knora response corresponds to the - * the class definition. - * - * @param instanceInspector an [[InstanceInspector]] for working with instances in a particular format. - */ + * A test utility for checking whether an instance of an RDF class returned in a Knora response corresponds to the + * the class definition. + * + * @param instanceInspector an [[InstanceInspector]] for working with instances in a particular format. + */ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Checks that an instance of an RDF class returned in a Knora response corresponds to the class definition. - * - * @param instanceResponse a Knora response containing the instance to be checked. - * @param expectedClassIri the IRI of the expected class. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - */ + * Checks that an instance of an RDF class returned in a Knora response corresponds to the class definition. + * + * @param instanceResponse a Knora response containing the instance to be checked. + * @param expectedClassIri the IRI of the expected class. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + */ def check(instanceResponse: String, expectedClassIri: SmartIri, knoraRouteGet: String => String): Unit = { val instanceElement: InstanceElement = instanceInspector.toElement(instanceResponse) val definitions: Definitions = getDefinitions(classIri = expectedClassIri, knoraRouteGet = knoraRouteGet) @@ -92,22 +85,22 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Logs an error message at DEBUG level and throws an [[AssertionException]] containing the message. - * - * @param msg the error message. - */ + * Logs an error message at DEBUG level and throws an [[AssertionException]] containing the message. + * + * @param msg the error message. + */ private def throwAndLogAssertionException(msg: String): Nothing = { logger.debug(msg) throw AssertionException(msg) } /** - * Recursively checks whether instance elements and their property objects correspond to their class definitions. - * - * @param instanceElement the instance to be checked. - * @param classIri the class IRI. - * @param definitions definitions of the class and any other relevant classes and properties. - */ + * Recursively checks whether instance elements and their property objects correspond to their class definitions. + * + * @param instanceElement the instance to be checked. + * @param classIri the class IRI. + * @param definitions definitions of the class and any other relevant classes and properties. + */ private def checkRec(instanceElement: InstanceElement, classIri: SmartIri, definitions: Definitions): Unit = { if (!instanceInspector.elementHasCompatibleType(element = instanceElement, expectedType = classIri, definitions = definitions)) { throwAndLogAssertionException(s"Instance type ${instanceElement.elementType} is not compatible with expected class IRI $classIri") @@ -181,7 +174,7 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging // It's a class that Knora doesn't serve. Accept the object only if it's an IRI. throwAndLogAssertionException(s"Property $propertyIri requires an IRI referring to an instance of $objectType, but object content was received instead") } - } else if (!objectType.isClientCollectionTypeIri) { + } else { // We're expecting a literal. Ask the element inspector if the object is compatible with // the expected type. if (!instanceInspector.elementHasCompatibleType(element = obj, expectedType = objectType, definitions = definitions)) { @@ -193,24 +186,24 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Gets all the definitions needed to check an instance of the specified class. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return a [[Definitions]] instance containing the relevant definitions. - */ + * Gets all the definitions needed to check an instance of the specified class. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return a [[Definitions]] instance containing the relevant definitions. + */ private def getDefinitions(classIri: SmartIri, knoraRouteGet: String => String): Definitions = { getDefinitionsRec(classIri = classIri, knoraRouteGet = knoraRouteGet, definitions = Definitions()) } /** - * Recursively gets definitions that are needed to check an instance of the specified class. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @param definitions the definitions collected so far. - * @return a [[Definitions]] instance containing the relevant definitions. - */ + * Recursively gets definitions that are needed to check an instance of the specified class. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @param definitions the definitions collected so far. + * @return a [[Definitions]] instance containing the relevant definitions. + */ private def getDefinitionsRec(classIri: SmartIri, knoraRouteGet: String => String, definitions: Definitions): Definitions = { // Get the definition of classIri. val classDef: ClassInfoContentV2 = getClassDef(classIri = classIri, knoraRouteGet = knoraRouteGet) @@ -254,36 +247,32 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging // Recursively add the definitions of base classes and classes that we identified as object types. classIrisForRecursion.foldLeft(updatedDefs) { case (acc, classIriFromObjectType) => - if (classIriFromObjectType.isClientCollectionTypeIri) { - acc - } else { - val recDefinitions: Definitions = getDefinitionsRec( - classIri = classIriFromObjectType, - knoraRouteGet = knoraRouteGet, - definitions = acc - ) - - acc.copy( - classDefs = acc.classDefs ++ recDefinitions.classDefs, - propertyDefs = acc.propertyDefs ++ recDefinitions.propertyDefs - ) - } + val recDefinitions: Definitions = getDefinitionsRec( + classIri = classIriFromObjectType, + knoraRouteGet = knoraRouteGet, + definitions = acc + ) + + acc.copy( + classDefs = acc.classDefs ++ recDefinitions.classDefs, + propertyDefs = acc.propertyDefs ++ recDefinitions.propertyDefs + ) } } /** - * Determines whether Knora should have the definition of a class. - * - * @param classIri the class IRI. - * @return `true` if Knora should have the definition of the class. - */ + * Determines whether Knora should have the definition of a class. + * + * @param classIri the class IRI. + * @return `true` if Knora should have the definition of the class. + */ private def isKnoraDefinedClass(classIri: SmartIri): Boolean = { // There are some Knora classes whose definitions we refer to but don't actually serve, e.g. // knora-api:XMLToStandoffMapping. The ontology transformation rules list the internal classes that // aren't available in each external schema. But these lists include knora-base classes // that are converted to custom datatypes in the simple schema, so we first need to check // if we're talking about one of those. - if (classIri.isKnoraApiV2EntityIri && !classIri.isClientCollectionTypeIri) { + if (classIri.isKnoraApiV2EntityIri) { val objectTypeInternal = classIri.toOntologySchema(InternalSchema) classIri.getOntologySchema.get match { @@ -300,8 +289,7 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } case ApiV2Complex => - if (!(KnoraBaseToApiV2ComplexTransformationRules.internalClassesToRemove.contains(objectTypeInternal) || - KnoraAdminToApiV2ComplexTransformationRules.internalClassesToRemove.contains(objectTypeInternal))) { + if (!KnoraBaseToApiV2ComplexTransformationRules.internalClassesToRemove.contains(objectTypeInternal)) { // It isn't one of the classes removed from the schema. true } else { @@ -317,12 +305,12 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Gets a class definition from Knora. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return the class definition. - */ + * Gets a class definition from Knora. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return the class definition. + */ private def getClassDef(classIri: SmartIri, knoraRouteGet: String => String): ClassInfoContentV2 = { InstanceChecker.classDefCache.get(classIri) match { case Some(classDef) => classDef @@ -345,12 +333,12 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Gets a property definition from Knora. - * - * @param propertyIri the property IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return the property definition. - */ + * Gets a property definition from Knora. + * + * @param propertyIri the property IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return the property definition. + */ private def getPropertyDef(propertyIri: SmartIri, knoraRouteGet: String => String): PropertyInfoContentV2 = { InstanceChecker.propertyDefCache.get(propertyIri) match { case Some(propertyDef) => propertyDef @@ -373,11 +361,11 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Gets the `knora-api:objectType` from a property definition in an external schema. - * - * @param propertyDef the property definition. - * @return the property's `knora-api:objectType`, if it has one. - */ + * Gets the `knora-api:objectType` from a property definition in an external schema. + * + * @param propertyDef the property definition. + * @return the property's `knora-api:objectType`, if it has one. + */ private def getObjectType(propertyDef: PropertyInfoContentV2): Option[SmartIri] = { propertyDef.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri). orElse(propertyDef.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri)) @@ -385,172 +373,45 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Represents an instance of an RDF class, or an element of such an instance. - * - * @param elementType the element type. This is an opaque string that only the [[InstanceInspector]] needs - * to understand. - * @param literalContent the literal content of the element (if available), for debugging. - * @param propertyObjects a map of property names to their objects. - */ + * Represents an instance of an RDF class, or an element of such an instance. + * + * @param elementType the element type. This is an opaque string that only the [[InstanceInspector]] needs + * to understand. + * @param literalContent the literal content of the element (if available), for debugging. + * @param propertyObjects a map of property names to their objects. + */ case class InstanceElement(elementType: String, literalContent: Option[String] = None, propertyObjects: Map[String, Vector[InstanceElement]] = Map.empty) /** - * A trait for classes that help [[InstanceChecker]] check instances in different formats. - */ + * A trait for classes that help [[InstanceChecker]] check instances in different formats. + */ trait InstanceInspector { /** - * Converts a Knora response representing a class instance to an [[InstanceElement]]. - */ + * Converts a Knora response representing a class instance to an [[InstanceElement]]. + */ def toElement(instanceResponse: String): InstanceElement /** - * Converts an RDF property IRI to the property name used in instances. - */ + * Converts an RDF property IRI to the property name used in instances. + */ def propertyIriToInstancePropertyName(classIri: SmartIri, propertyIri: SmartIri): String /** - * Returns `true` if the specified instance element is an IRI pointing to an object, as opposed to - * the object itself. - */ + * Returns `true` if the specified instance element is an IRI pointing to an object, as opposed to + * the object itself. + */ def elementIsIri(element: InstanceElement): Boolean /** - * Checks, as far as possible, whether the type of an instance is compatible with the type IRI of its class. - */ + * Checks, as far as possible, whether the type of an instance is compatible with the type IRI of its class. + */ def elementHasCompatibleType(element: InstanceElement, expectedType: SmartIri, definitions: Definitions): Boolean } /** - * Constants for working with instances parsed from JSON. - */ -object JsonInstanceInspector { - val STRING = "String" - val IRI = "IRI" - val INTEGER = "Integer" - val DECIMAL = "Decimal" - val BOOLEAN = "Boolean" - val OBJECT = "Object" - - val LiteralTypeMap: Map[String, Set[IRI]] = Map( - STRING -> - Set(OntologyConstants.Xsd.String, - OntologyConstants.Xsd.DateTimeStamp, - OntologyConstants.KnoraApiV2Simple.Date, - OntologyConstants.KnoraApiV2Simple.Geom, - OntologyConstants.KnoraApiV2Simple.Color, - OntologyConstants.KnoraApiV2Simple.Interval, - OntologyConstants.KnoraApiV2Simple.Geoname, - OntologyConstants.KnoraApiV2Simple.ListNode), - - IRI -> Set(OntologyConstants.Xsd.Uri, OntologyConstants.KnoraApiV2Simple.File), - INTEGER -> Set(OntologyConstants.Xsd.Integer, OntologyConstants.Xsd.NonNegativeInteger), - DECIMAL -> Set(OntologyConstants.Xsd.Decimal), - BOOLEAN -> Set(OntologyConstants.Xsd.Boolean) - ) - - val LiteralTypeIris: Set[IRI] = LiteralTypeMap.values.flatten.toSet -} - -/** - * An [[InstanceInspector]] that works with Knora responses in JSON format. - */ -class JsonInstanceInspector extends InstanceInspector { - - import JsonInstanceInspector._ - - implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - private val nonStandardJsonPropertyNames: Map[SmartIri, Map[SmartIri, String]] = AdminClientApi.propertyNames - - override def toElement(response: String): InstanceElement = { - jsObjectToElement(JsonParser(response).asJsObject) - } - - private def jsValueToElements(jsValue: JsValue): Vector[InstanceElement] = { - jsValue match { - case jsObject: JsObject => Vector(jsObjectToElement(jsObject)) - case jsArray: JsArray => jsArray.elements.flatMap(jsValue => jsValueToElements(jsValue)) - - case jsString: JsString => - // Does this string represent an IRI? - val strType = if (stringFormatter.isIri(jsString.value)) { - // Yes. Store its type as IRI. - IRI - } else { - // No. Store its type as STRING. - STRING - } - - Vector(InstanceElement(elementType = strType, literalContent = Some(jsString.value))) - - case jsNumber: JsNumber => - // Is this an integer? - val numericType = if (jsNumber.value.isWhole) { - // Yes. Store its type as INTEGER. - INTEGER - } else { - // No. Store its type as DECIMAL. - DECIMAL - } - - Vector(InstanceElement(elementType = numericType, literalContent = Some(jsNumber.value.toString))) - - case jsBoolean: JsBoolean => - Vector(InstanceElement(elementType = BOOLEAN, literalContent = Some(jsBoolean.value.toString))) - - case JsNull => Vector.empty - } - } - - private def jsObjectToElement(jsObject: JsObject): InstanceElement = { - val propertyObjects = jsObject.fields.map { - case (key: String, jsValue: JsValue) => key -> jsValueToElements(jsValue) - } - - InstanceElement(elementType = OBJECT, propertyObjects = propertyObjects) - } - - override def propertyIriToInstancePropertyName(classIri: SmartIri, propertyIri: SmartIri): String = { - // If this property IRI has a non-standard name in this class, return that name. Otherwise, - // just remove the namespace. - nonStandardJsonPropertyNames.get(classIri) match { - case Some(propertyMap) => - propertyMap.get(propertyIri) match { - case Some(propertyName) => propertyName - case None => propertyIri.getEntityName - } - - case None => propertyIri.getEntityName - } - } - - override def elementIsIri(element: InstanceElement): Boolean = { - element.elementType == IRI - } - - override def elementHasCompatibleType(element: InstanceElement, expectedType: SmartIri, definitions: Definitions): Boolean = { - // Don't try to check collection types. - if (expectedType.isClientCollectionTypeIri) { - true - } else { - // Is the element a literal? - LiteralTypeMap.get(element.elementType) match { - case Some(typeIris) => - // Yes. It's compatible if the the expected type is one of the types that it could represent. - typeIris.contains(expectedType.toString) - - case None => - // No. It's compatible if the expected type isn't a literal type. - !LiteralTypeIris.contains(expectedType.toString) - } - } - } -} - -/** - * An [[InstanceInspector]] that works with Knora responses in JSON-LD format. - */ + * An [[InstanceInspector]] that works with Knora responses in JSON-LD format. + */ class JsonLDInstanceInspector extends InstanceInspector { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -618,33 +479,29 @@ class JsonLDInstanceInspector extends InstanceInspector { } override def elementHasCompatibleType(element: InstanceElement, expectedType: SmartIri, definitions: Definitions): Boolean = { - if (expectedType.isClientCollectionTypeIri) { - true + val expectedTypeStr: IRI = expectedType.toString + + // Is the element's type a Knora class? + if (expectedType.isKnoraApiV2EntityIri) { + // Yes. It's compatible if its type is a subclass of the expected type. + definitions.getBaseClasses(element.elementType.toSmartIri).contains(expectedType) } else { - val expectedTypeStr: IRI = expectedType.toString - - // Is the element's type a Knora class? - if (expectedType.isKnoraApiV2EntityIri) { - // Yes. It's compatible if its type is a subclass of the expected type. - definitions.getBaseClasses(element.elementType.toSmartIri).contains(expectedType) - } else { - // The element's type isn't a Knora class. It's compatible if it's the same as the expected type, or if - // we expected an xsd:integer and we got an xsd:nonNegativeInteger. - element.elementType == expectedTypeStr || - (element.elementType == OntologyConstants.Xsd.NonNegativeInteger && expectedTypeStr == OntologyConstants.Xsd.Integer) - } + // The element's type isn't a Knora class. It's compatible if it's the same as the expected type, or if + // we expected an xsd:integer and we got an xsd:nonNegativeInteger. + element.elementType == expectedTypeStr || + (element.elementType == OntologyConstants.Xsd.NonNegativeInteger && expectedTypeStr == OntologyConstants.Xsd.Integer) } } } /** - * Represents Knora ontology definitions used to check class instances against their definitions. - * - * @param classDefs relevant class definitions. - * @param propertyDefs relevant property definitions. - * @param subClassOf a map in which each class IRI points to the full set of its base classes. A class is also - * a subclass of itself. - */ + * Represents Knora ontology definitions used to check class instances against their definitions. + * + * @param classDefs relevant class definitions. + * @param propertyDefs relevant property definitions. + * @param subClassOf a map in which each class IRI points to the full set of its base classes. A class is also + * a subclass of itself. + */ case class Definitions(classDefs: Map[SmartIri, ClassInfoContentV2] = Map.empty, propertyDefs: Map[SmartIri, PropertyInfoContentV2] = Map.empty, subClassOf: Map[SmartIri, Set[SmartIri]] = Map.empty) { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala index 01d509cc7d..a3d1276c6b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala @@ -43,7 +43,6 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { implicit val ec: ExecutionContextExecutor = system.dispatcher private val jsonLDInstanceChecker: InstanceChecker = InstanceChecker.getJsonLDChecker - private val jsonInstanceChecker: InstanceChecker = InstanceChecker.getJsonChecker "The InstanceChecker" should { "accept a JSON-LD instance of anything:Thing" in { @@ -139,50 +138,6 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { assert(exception.getMessage == "Property http://www.w3.org/2000/01/rdf-schema#label has 0 objects, but its cardinality is 1") } - - "accept a correct JSON instance of an admin:User" in { - jsonInstanceChecker.check( - instanceResponse = InstanceCheckerSpec.correctUser, - expectedClassIri = "http://api.knora.org/ontology/knora-admin/v2#User".toSmartIri, - knoraRouteGet = doGetRequest - ) - } - - "reject a JSON instance of an admin:User with an extra property" in { - val exception = intercept[AssertionException] { - jsonInstanceChecker.check( - instanceResponse = InstanceCheckerSpec.userWithExtraProperty, - expectedClassIri = "http://api.knora.org/ontology/knora-admin/v2#User".toSmartIri, - knoraRouteGet = doGetRequest - ) - } - - assert(exception.getMessage == "One or more instance properties are not allowed by cardinalities: extraProperty") - } - - "reject a JSON instance of an admin:User without a username" in { - val exception = intercept[AssertionException] { - jsonInstanceChecker.check( - instanceResponse = InstanceCheckerSpec.userWithMissingUsername, - expectedClassIri = "http://api.knora.org/ontology/knora-admin/v2#User".toSmartIri, - knoraRouteGet = doGetRequest - ) - } - - assert(exception.getMessage == "Property username has 0 objects, but its cardinality is 1") - } - - "reject a JSON instance of an admin:User with an invalid literal object type" in { - val exception = intercept[AssertionException] { - jsonInstanceChecker.check( - instanceResponse = InstanceCheckerSpec.userWithInvalidObjectType, - expectedClassIri = "http://api.knora.org/ontology/knora-admin/v2#User".toSmartIri, - knoraRouteGet = doGetRequest - ) - } - - assert(exception.getMessage == "Property status has an object of type String with literal content 'invalidValue', but type http://www.w3.org/2001/XMLSchema#boolean was expected") - } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala index bbb4b4420f..373677bb0f 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala @@ -372,7 +372,7 @@ class ProjectsADME2ESpec extends E2ESpec(ProjectsADME2ESpec.config) with Session assert(response.status === StatusCodes.OK) val keywords: Seq[String] = AkkaHttpUtils.httpResponseToJson(response).fields("keywords").convertTo[Seq[String]] - keywords.size should be (18) + keywords.size should be (20) } "return all keywords for a single project" in { @@ -387,8 +387,8 @@ class ProjectsADME2ESpec extends E2ESpec(ProjectsADME2ESpec.config) with Session } "return empty list for a project without keywords" in { - val anythingIriEnc = URLEncoder.encode(SharedTestDataADM.anythingProject.id, "utf-8") - val request = Get(baseApiUrl + s"/admin/projects/iri/$anythingIriEnc/Keywords") ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) + val dokubibIriEnc = URLEncoder.encode(SharedTestDataADM.dokubibProject.id, "utf-8") + val request = Get(baseApiUrl + s"/admin/projects/iri/$dokubibIriEnc/Keywords") ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index 0bda3bdabe..e9af06628d 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -549,7 +549,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return all unique keywords for all projects" in { responderManager ! ProjectsKeywordsGetRequestADM(SharedTestDataADM.rootUser) val received: ProjectsKeywordsGetResponseADM = expectMsgType[ProjectsKeywordsGetResponseADM](timeout) - received.keywords.size should be (18) + received.keywords.size should be (20) } "return all keywords for a single project" in { @@ -563,7 +563,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return empty list for a project without keywords" in { responderManager ! ProjectKeywordsGetRequestADM( - projectIri = SharedTestDataADM.anythingProject.id, + projectIri = SharedTestDataADM.dokubibProject.id, requestingUser = SharedTestDataADM.rootUser ) val received: ProjectKeywordsGetResponseADM = expectMsgType[ProjectKeywordsGetResponseADM](timeout) diff --git a/webapi/src/test/scala/org/knora/webapi/util/StringFormatterSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/StringFormatterSpec.scala index f4c34afed7..0f55b9e9b3 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/StringFormatterSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/StringFormatterSpec.scala @@ -25,7 +25,6 @@ import java.util.UUID import org.knora.webapi._ import org.knora.webapi.util.IriConversions._ import org.knora.webapi.util.StringFormatter.SalsahGuiAttributeDefinition -import org.knora.webapi.util.clientapi.{ArrayType, ClassRef, MapType, UriDatatype} /** * Tests [[StringFormatter]]. @@ -886,30 +885,6 @@ class StringFormatterSpec extends CoreSpec() { assert(isResource) } - "parse a type annotation representing a collection type for use in generated client code" in { - val ontologyIri: SmartIri = OntologyConstants.KnoraAdminV2.KnoraAdminOntologyIri.toSmartIri - val permissionClassIri: SmartIri = ontologyIri.makeEntityIri("Permission") - val permissionClassRef = ClassRef(className = "Permission", classIri = permissionClassIri) - val collectionTypeIri = ontologyIri.makeEntityIri("collection: Array[Map[URI, Array[Map[URI, Permission]]]]") - - assert(collectionTypeIri.isClientCollectionTypeIri) - val collectionType = collectionTypeIri.getClientCollectionType - - assert(collectionType == ArrayType( - elementType = MapType( - keyType = UriDatatype, - valueType = ArrayType( - elementType = MapType( - keyType = UriDatatype, - valueType = permissionClassRef - ) - ) - ) - )) - - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - "convert 100,000 IRIs" ignore { val totalIris = 100000 diff --git a/webapi/src/test/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParserSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParserSpec.scala deleted file mode 100644 index f53485f06e..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/util/clientapi/ClientCollectionTypeParserSpec.scala +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import org.knora.webapi.util.IriConversions._ -import org.knora.webapi.util.{SmartIri, StringFormatter} -import org.knora.webapi.{ClientApiGenerationException, CoreSpec, OntologyConstants} - -/** - * Tests [[ClientCollectionTypeParser]]. - */ -class ClientCollectionTypeParserSpec extends CoreSpec() { - private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val ontologyIri: SmartIri = OntologyConstants.KnoraAdminV2.KnoraAdminOntologyIri.toSmartIri - private val permissionClassIri: SmartIri = ontologyIri.makeEntityIri("Permission") - private val permissionClassRef = ClassRef(className = "Permission", classIri = permissionClassIri) - - "The ClientCollectionTypeParser class" should { - "parse Array[String]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Array[String]", ontologyIri = ontologyIri) - assert(collectionType == ArrayType(elementType = StringDatatype)) - assert(collectionType.getClassIri.isEmpty) - } - - "parse Array[URI]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Array[URI]", ontologyIri = ontologyIri) - assert(collectionType == ArrayType(elementType = UriDatatype)) - assert(collectionType.getClassIri.isEmpty) - } - - "parse Array[Permission]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Array[Permission]", ontologyIri = ontologyIri) - assert(collectionType == ArrayType(elementType = permissionClassRef)) - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - - "parse Map[String, String]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[String, String]", ontologyIri = ontologyIri) - assert(collectionType == MapType(keyType = StringDatatype, valueType = StringDatatype)) - assert(collectionType.getClassIri.isEmpty) - } - - "parse Map[URI, String]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[URI, String]", ontologyIri = ontologyIri) - assert(collectionType == MapType(keyType = UriDatatype, valueType = StringDatatype)) - assert(collectionType.getClassIri.isEmpty) - } - - "parse Map[URI, Permission]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[URI, Permission]", ontologyIri = ontologyIri) - - assert(collectionType == MapType( - keyType = UriDatatype, - valueType = permissionClassRef - )) - - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - - "parse Map[URI, Array[String]]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[URI, Array[String]]", ontologyIri = ontologyIri) - - assert(collectionType == MapType( - keyType = UriDatatype, - valueType = ArrayType(elementType = StringDatatype) - )) - - assert(collectionType.getClassIri.isEmpty) - } - - "parse Map[URI, Array[Permission]]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[URI, Array[Permission]]", ontologyIri = ontologyIri) - - assert(collectionType == MapType( - keyType = UriDatatype, - valueType = ArrayType(elementType = permissionClassRef) - )) - - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - - "parse Map[URI, Array[Map[URI, Permission]]]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Map[URI, Array[Map[URI, Permission]]]", ontologyIri = ontologyIri) - - assert(collectionType == MapType( - keyType = UriDatatype, - valueType = ArrayType( - elementType = MapType( - keyType = UriDatatype, - valueType = permissionClassRef - ) - ) - )) - - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - - "parse Array[Map[URI, Array[Map[URI, Permission]]]]" in { - val collectionType = ClientCollectionTypeParser.parse(typeStr = "Array[Map[URI, Array[Map[URI, Permission]]]]", ontologyIri = ontologyIri) - - assert(collectionType == ArrayType( - elementType = MapType( - keyType = UriDatatype, - valueType = ArrayType( - elementType = MapType( - keyType = UriDatatype, - valueType = permissionClassRef - ) - ) - ) - )) - - assert(collectionType.getClassIri.contains(permissionClassIri)) - } - - "reject String" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "String", ontologyIri = ontologyIri) - } - } - - "reject Array[]" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "Array[]", ontologyIri = ontologyIri) - } - } - - "reject Array[Map[URI, Array[Map[URI, Permission]]]" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "Array[Map[URI, Array[Map[URI, Permission]]]", ontologyIri = ontologyIri) - } - } - - "reject Map[String, ]" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "Map[String, ]", ontologyIri = ontologyIri) - } - } - - "reject []" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "[]", ontologyIri = ontologyIri) - } - } - - "reject Array[String],Array[String]" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = "Array[String],Array[String]", ontologyIri = ontologyIri) - } - } - - "reject ,Array[String]" in { - assertThrows[ClientApiGenerationException] { - ClientCollectionTypeParser.parse(typeStr = ",Array[String]", ontologyIri = ontologyIri) - } - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/util/clientapi/SourceCodeFilePathSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/clientapi/SourceCodeFilePathSpec.scala deleted file mode 100644 index efb22fbd32..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/util/clientapi/SourceCodeFilePathSpec.scala +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © 2015-2019 the contributors (see Contributors.md). - * - * This file is part of Knora. - * - * Knora is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Knora is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with Knora. If not, see . - */ - -package org.knora.webapi.util.clientapi - -import org.knora.webapi.CoreSpec - -class SourceCodeFilePathSpec extends CoreSpec() { - "The SourceCodeFilePath class" should { - "walk one directory up, then one directory down" in { - val sourcePath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz"), - filename = "test1", - fileExtension = "js" - ) - - val targetPath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "quux"), - filename = "test2", - fileExtension = "js" - ) - - assert(sourcePath.makeImportPath(targetPath) == "../quux/test2.js") - assert(sourcePath.makeImportPath(targetPath, includeFileExtension = false) == "../quux/test2") - } - - "walk two directories up, then three directories down" in { - val sourcePath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz", "quux"), - filename = "test1", - fileExtension = "js" - ) - - val targetPath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "corge", "grault", "garply"), - filename = "test2", - fileExtension = "js" - ) - - assert(sourcePath.makeImportPath(targetPath) == "../../corge/grault/garply/test2.js") - } - - "walk down only" in { - val sourcePath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar"), - filename = "test1", - fileExtension = "js" - ) - - val targetPath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz"), - filename = "test2", - fileExtension = "js" - ) - - assert(sourcePath.makeImportPath(targetPath) == "./baz/test2.js") - } - - "walk up only" in { - val sourcePath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz"), - filename = "test1", - fileExtension = "js" - ) - - val targetPath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar"), - filename = "test2", - fileExtension = "js" - ) - - assert(sourcePath.makeImportPath(targetPath) == "../test2.js") - } - - "use the same directory" in { - val sourcePath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz"), - filename = "test1", - fileExtension = "js" - ) - - val targetPath = SourceCodeFilePath( - directoryPath = Seq("foo", "bar", "baz"), - filename = "test2", - fileExtension = "js" - ) - - assert(sourcePath.makeImportPath(targetPath) == "./test2.js") - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/util/jsonld/JsonLDUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/jsonld/JsonLDUtilSpec.scala index ade1640088..30fa2996ee 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/jsonld/JsonLDUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/jsonld/JsonLDUtilSpec.scala @@ -19,13 +19,16 @@ package org.knora.webapi.util.jsonld -import org.knora.webapi.CoreSpec +import org.knora.webapi.util.StringFormatter +import org.scalatest.{Matchers, WordSpecLike} import spray.json.{JsValue, JsonParser} /** * Tests [[JsonLDUtil]]. */ -class JsonLDUtilSpec extends CoreSpec { +class JsonLDUtilSpec extends WordSpecLike with Matchers { + + StringFormatter.initForTest() "The JSON-LD utility" should { "parse JSON-LD text, compact it with an empty context, convert the result to a JsonLDDocument, and convert that back to text" in {