Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Domain endpoint for compiling templates to schemas #63

Merged
merged 17 commits into from
Jan 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/schema-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Schema Compilation Workflow

To use a GraphQL Schema with data, there are two main steps:

- Adding Entities: Creating `schemaEntityType`s that will make up the Schema
- Compilation: Generating a `schemaDomainType`

## Adding Entities

Entities can be created, updated, modified, and deleted through the `/_plugins/_simpleschema/object` endpoint.
Creation is done by `POST`ing to this endpoint with a Json object.
The object must have a `type` of `schemaEntityType`.

```json
{
"schemaEntityType": {
"name": "Author",
"objectId": "testAuthorObject",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change objectId to name

"type": "schemaEntityType",
"catalog": ["library"],
"content": "type Author { id: ID! \n name: String! \n born: DateTime! \n died: DateTime \n nationality: String! \n books: [Book] \n }"
}
}
```

When the object is created, if one is not provided, it will be assigned an `objectId` primary key.

## Compilation

Compiled GraphQL Schemas are stored as `Domain` resources,
available at the `/_plugins/_simpleschema/domain` endpoint.
As `Domain`s must be uniquely named, they are given an `objectId` instead of a `name`.
Swiddis marked this conversation as resolved.
Show resolved Hide resolved

Provide the entity list as a list of `objectId`s to be used in compilation.

```json
{
"objectId": "testSchema",
Swiddis marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change objectId to name

"catalog": ["library"],
"entityList": ["testAuthorObject", "testBookObject"]
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ import org.opensearch.repositories.RepositoriesService
import org.opensearch.rest.RestController
import org.opensearch.rest.RestHandler
import org.opensearch.script.ScriptService
import org.opensearch.simpleschema.action.CreateSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.DeleteSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.GetSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.UpdateSimpleSchemaObjectAction
import org.opensearch.simpleschema.index.SimpleSearchIndex
import org.opensearch.simpleschema.resthandler.SimpleSchemaRestHandler
import org.opensearch.simpleschema.settings.PluginSettings
Expand All @@ -38,9 +34,16 @@ import org.opensearch.watcher.ResourceWatcherService
import java.util.function.Supplier
import org.opensearch.jobscheduler.spi.ScheduledJobParser
import org.opensearch.jobscheduler.spi.ScheduledJobRunner
import org.opensearch.simpleschema.action.CreateSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.DeleteSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.GetSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.UpdateSimpleSchemaObjectAction
import org.opensearch.simpleschema.action.CreateSimpleSchemaDomainAction
import org.opensearch.simpleschema.action.GetSimpleSchemaDomainAction
import org.opensearch.simpleschema.scheduler.SimpleSearchJobParser
import org.opensearch.simpleschema.scheduler.SimpleSearchJobRunner
import org.opensearch.simpleschema.resthandler.SchedulerRestHandler
import org.opensearch.simpleschema.resthandler.SimpleSchemaDomainRestHandler


/**
Expand Down Expand Up @@ -97,6 +100,7 @@ class SimpleSchemaPlugin : Plugin(), ActionPlugin, JobSchedulerExtension {
): List<RestHandler> {
return listOf(
SimpleSchemaRestHandler(),
SimpleSchemaDomainRestHandler(),
)
}

Expand All @@ -120,6 +124,14 @@ class SimpleSchemaPlugin : Plugin(), ActionPlugin, JobSchedulerExtension {
ActionPlugin.ActionHandler(
UpdateSimpleSchemaObjectAction.ACTION_TYPE,
UpdateSimpleSchemaObjectAction::class.java
),
ActionPlugin.ActionHandler(
CreateSimpleSchemaDomainAction.ACTION_TYPE,
CreateSimpleSchemaDomainAction::class.java
),
ActionPlugin.ActionHandler(
GetSimpleSchemaDomainAction.ACTION_TYPE,
GetSimpleSchemaDomainAction::class.java
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.opensearch.simpleschema.action

import org.opensearch.action.ActionType
import org.opensearch.action.support.ActionFilters
import org.opensearch.client.Client
import org.opensearch.common.inject.Inject
import org.opensearch.common.xcontent.NamedXContentRegistry
import org.opensearch.commons.authuser.User
import org.opensearch.transport.TransportService

internal class CreateSimpleSchemaDomainAction @Inject constructor(
transportService: TransportService,
client: Client,
actionFilters: ActionFilters,
val xContentRegistry: NamedXContentRegistry
) : PluginBaseAction<CreateSimpleSchemaDomainRequest, CreateSimpleSchemaDomainResponse>(
NAME,
transportService,
client,
actionFilters,
::CreateSimpleSchemaDomainRequest
) {
companion object {
private const val NAME = "cluster:admin/opensearch/simpleschema/domain/create"
internal val ACTION_TYPE = ActionType(NAME, ::CreateSimpleSchemaDomainResponse)
}

/**
* {@inheritDoc}
*/
override fun executeRequest(
request: CreateSimpleSchemaDomainRequest,
user: User?
): CreateSimpleSchemaDomainResponse {
return SimpleSchemaDomainActions.create(request, user)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.opensearch.simpleschema.action

import org.opensearch.action.ActionRequest
import org.opensearch.action.ActionRequestValidationException
import org.opensearch.common.io.stream.StreamInput
import org.opensearch.common.io.stream.Writeable
import org.opensearch.common.xcontent.ToXContentObject
import org.opensearch.common.xcontent.XContentParser
import org.opensearch.common.xcontent.XContentBuilder
import org.opensearch.common.xcontent.ToXContent
import org.opensearch.common.xcontent.XContentParserUtils
import org.opensearch.commons.utils.logger
import org.opensearch.commons.utils.stringList
import org.opensearch.simpleschema.domain.DomainRepository
import org.opensearch.simpleschema.domain.DomainResource
import org.opensearch.simpleschema.model.RestTag
import org.opensearch.simpleschema.model.SchemaCompilationType
import java.io.IOException

internal class CreateSimpleSchemaDomainRequest : ActionRequest, ToXContentObject {
val name: String
val entities: List<String>

companion object {
private val log by logger(CreateSimpleSchemaDomainRequest::class.java)

/**
* reader to create instance of class from writable.
*/
val reader = Writeable.Reader { CreateSimpleSchemaDomainRequest(it) }

/**
* Creator used in REST communication.
* @param parser XContentParser to deserialize data from.
*/
@JvmStatic
@Throws(IOException::class)
fun parse(parser: XContentParser): CreateSimpleSchemaDomainRequest {
var name: String? = null
var entities: List<String>? = null

XContentParserUtils.ensureExpectedToken(
XContentParser.Token.START_OBJECT,
parser.currentToken(),
parser
)
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
val fieldName = parser.currentName()
parser.nextToken()
when (fieldName) {
RestTag.NAME_FIELD -> name = parser.text()
RestTag.ENTITY_LIST_FIELD -> entities = parser.stringList()
else -> {
parser.skipChildren()
log.info("Unexpected field: $fieldName, while parsing CreateDomainRequest")
}
}
}
name ?: throw IllegalArgumentException("Required field '${RestTag.NAME_FIELD}' is absent")
entities ?: throw IllegalArgumentException("Required field '${RestTag.ENTITY_LIST_FIELD}' is absent")
return CreateSimpleSchemaDomainRequest(name, entities)
}
}

constructor(name: String, entities: List<String>) {
this.name = name
this.entities = entities
}

/**
* {@inheritDoc}
*/
@Throws(IOException::class)
constructor(input: StreamInput) : super(input) {
name = input.readString()
entities = input.readStringList()
}

/**
* {@inheritDoc}
*/
override fun validate(): ActionRequestValidationException? {
// TODO currently no validation
return null
Swiddis marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* {@inheritDoc}
*/
override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
builder!!
builder.startObject()
.field(RestTag.NAME_FIELD, name)
.field(RestTag.ENTITY_LIST_FIELD, entities)
val domain = DomainRepository.getDomain(name)
if (domain != null) {
builder.field("domain")
domain.toXContent(builder, params)
}
return builder.endObject()
}

fun toObjectData(): SchemaCompilationType {
return SchemaCompilationType(name, entities, null, null)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.opensearch.simpleschema.action

import org.opensearch.common.io.stream.StreamInput
import org.opensearch.common.io.stream.StreamOutput
import org.opensearch.common.io.stream.Writeable
import org.opensearch.common.xcontent.ToXContent
import org.opensearch.common.xcontent.XContentBuilder
import org.opensearch.common.xcontent.XContentParser
import org.opensearch.common.xcontent.XContentParserUtils
import org.opensearch.commons.utils.logger
import org.opensearch.simpleschema.domain.DomainRepository
import org.opensearch.simpleschema.model.BaseResponse
import org.opensearch.simpleschema.model.RestTag
import java.io.IOException

internal class CreateSimpleSchemaDomainResponse : BaseResponse {
private var objectId: String
Swiddis marked this conversation as resolved.
Show resolved Hide resolved
private var name: String
private var entities: List<String>

/**
* constructor for creating the class
* @param id the id of the created Object
*/
constructor(objectId: String, name: String, entities: List<String>) {
this.objectId = objectId
this.name = name
this.entities = entities
}

/**
* {@inheritDoc}
*/
@Throws(IOException::class)
constructor(input: StreamInput) : super(input) {
objectId = input.readString()
name = input.readString()
entities = input.readStringList()
}

/**
* {@inheritDoc}
*/
@Throws(IOException::class)
override fun writeTo(output: StreamOutput) {
output.writeString(objectId)
output.writeString(name)
output.writeStringArray(entities.toTypedArray())
}

/**
* {@inheritDoc}
*/
override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
builder!!
return builder.startObject()
.field(RestTag.OBJECT_ID_FIELD, objectId)
.field(RestTag.NAME_FIELD, name)
.field(RestTag.ENTITY_LIST_FIELD, entities)
.endObject()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.opensearch.simpleschema.action

import org.opensearch.action.ActionType
import org.opensearch.action.support.ActionFilters
import org.opensearch.client.Client
import org.opensearch.common.inject.Inject
import org.opensearch.common.xcontent.NamedXContentRegistry
import org.opensearch.commons.authuser.User
import org.opensearch.transport.TransportService

internal class GetSimpleSchemaDomainAction @Inject constructor(
transportService: TransportService,
client: Client,
actionFilters: ActionFilters,
val xContentRegistry: NamedXContentRegistry
) : PluginBaseAction<GetSimpleSchemaDomainRequest, GetSimpleSchemaDomainResponse>(
NAME,
transportService,
client,
actionFilters,
::GetSimpleSchemaDomainRequest
) {
companion object {
private const val NAME = "cluster:admin/opensearch/simpleschema/domain/get"
internal val ACTION_TYPE = ActionType(NAME, ::GetSimpleSchemaDomainResponse)
}

/**
* {@inheritDoc}
*/
override fun executeRequest(
request: GetSimpleSchemaDomainRequest,
user: User?
): GetSimpleSchemaDomainResponse {
return SimpleSchemaDomainActions.get(request, user)
}
}
Loading