From 0cc0d804cfed8cdae9ba5229eb81111388b4383a Mon Sep 17 00:00:00 2001 From: Sherif Nada Date: Wed, 15 Sep 2021 16:24:11 -0700 Subject: [PATCH] return auth spec in the API --- airbyte-api/src/main/openapi/config.yaml | 34 +++++++++++++ .../converters/OauthModelConverter.java | 50 +++++++++++++++++++ .../server/handlers/SchedulerHandler.java | 23 +++++++-- .../api/generated-api-html/index.html | 33 ++++++++++++ 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 airbyte-server/src/main/java/io/airbyte/server/converters/OauthModelConverter.java diff --git a/airbyte-api/src/main/openapi/config.yaml b/airbyte-api/src/main/openapi/config.yaml index 583a149090882..3c015b9b3b4ac 100644 --- a/airbyte-api/src/main/openapi/config.yaml +++ b/airbyte-api/src/main/openapi/config.yaml @@ -1906,6 +1906,34 @@ components: description: The specification for what values are required to configure the sourceDefinition. type: object example: { user: { type: string } } + SourceAuthSpecification: + $ref: "#/components/schemas/AuthSpecification" + AuthSpecification: + type: object + properties: + auth_type: + type: string + enum: ["oauth2.0"] # Future auth types should be added here + oauth2Specification: + "$ref": "#/components/schemas/OAuth2Specification" + OAuth2Specification: + description: An object containing any metadata needed to describe this connector's Oauth flow + type: object + properties: + oauthFlowInitParameters: + description: + "Pointers to the fields in the ConnectorSpecification which are needed to obtain the initial refresh/access tokens for the OAuth flow. + Each inner array represents the path in the ConnectorSpecification of the referenced field. + For example. + Assume the ConnectorSpecification contains params 'app_secret', 'app_id' which are needed to get the initial refresh token. + If they are not nested in the config, then the array would look like this [['app_secret'], ['app_id']] + If they are nested inside, say, an object called 'auth_params' then this array would be [['auth_params', 'app_secret'], ['auth_params', 'app_id']]" + type: array + items: + description: A list of strings which describes each parameter's path inside the ConnectionSpecification + type: array + items: + type: string SourceDefinitionSpecificationRead: type: object required: @@ -1918,6 +1946,8 @@ components: type: string connectionSpecification: $ref: "#/components/schemas/SourceDefinitionSpecification" + authSpecification: + $ref: "#/components/schemas/SourceAuthSpecification" jobInfo: $ref: "#/components/schemas/SynchronousJobRead" # SOURCE @@ -2018,6 +2048,8 @@ components: DestinationDefinitionId: type: string format: uuid + DestinationAuthSpecification: + $ref: "#/components/schemas/AuthSpecification" DestinationDefinitionIdRequestBody: type: object required: @@ -2101,6 +2133,8 @@ components: type: string connectionSpecification: $ref: "#/components/schemas/DestinationDefinitionSpecification" + authSpecification: + $ref: "#/components/schemas/DestinationAuthSpecification" jobInfo: $ref: "#/components/schemas/SynchronousJobRead" supportedDestinationSyncModes: diff --git a/airbyte-server/src/main/java/io/airbyte/server/converters/OauthModelConverter.java b/airbyte-server/src/main/java/io/airbyte/server/converters/OauthModelConverter.java new file mode 100644 index 0000000000000..b86ee4e1f45f6 --- /dev/null +++ b/airbyte-server/src/main/java/io/airbyte/server/converters/OauthModelConverter.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2020 Airbyte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.airbyte.server.converters; + +import io.airbyte.api.model.AuthSpecification; +import io.airbyte.api.model.OAuth2Specification; +import io.airbyte.protocol.models.ConnectorSpecification; +import java.util.Optional; + +public class OauthModelConverter { + + public static Optional getAuthSpec(ConnectorSpecification spec) { + if (spec.getAuthSpecification() == null) { + return Optional.empty(); + } + io.airbyte.protocol.models.AuthSpecification incomingAuthSpec = spec.getAuthSpecification(); + + AuthSpecification authSpecification = new AuthSpecification(); + if (incomingAuthSpec.getAuthType() == io.airbyte.protocol.models.AuthSpecification.AuthType.OAUTH_2_0) { + authSpecification.authType(AuthSpecification.AuthTypeEnum.OAUTH2_0) + .oauth2Specification(new OAuth2Specification() + .oauthFlowInitParameters(incomingAuthSpec.getOauth2Specification().getOauthFlowInitParameters())); + } + + return Optional.ofNullable(authSpecification); + } + +} diff --git a/airbyte-server/src/main/java/io/airbyte/server/handlers/SchedulerHandler.java b/airbyte-server/src/main/java/io/airbyte/server/handlers/SchedulerHandler.java index 8299d8c59dde8..d6a46858b754b 100644 --- a/airbyte-server/src/main/java/io/airbyte/server/handlers/SchedulerHandler.java +++ b/airbyte-server/src/main/java/io/airbyte/server/handlers/SchedulerHandler.java @@ -26,6 +26,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; +import io.airbyte.api.model.AuthSpecification; import io.airbyte.api.model.CheckConnectionRead; import io.airbyte.api.model.CheckConnectionRead.StatusEnum; import io.airbyte.api.model.ConnectionIdRequestBody; @@ -67,6 +68,7 @@ import io.airbyte.server.converters.CatalogConverter; import io.airbyte.server.converters.ConfigurationUpdate; import io.airbyte.server.converters.JobConverter; +import io.airbyte.server.converters.OauthModelConverter; import io.airbyte.server.converters.SpecFetcher; import io.airbyte.validation.json.JsonSchemaValidator; import io.airbyte.validation.json.JsonValidationException; @@ -246,11 +248,18 @@ public SourceDefinitionSpecificationRead getSourceDefinitionSpecification(Source final String imageName = DockerUtils.getTaggedImageName(source.getDockerRepository(), source.getDockerImageTag()); final SynchronousResponse response = getConnectorSpecification(imageName); final ConnectorSpecification spec = response.getOutput(); - return new SourceDefinitionSpecificationRead() + SourceDefinitionSpecificationRead specRead = new SourceDefinitionSpecificationRead() .jobInfo(JobConverter.getSynchronousJobRead(response)) .connectionSpecification(spec.getConnectionSpecification()) .documentationUrl(spec.getDocumentationUrl().toString()) .sourceDefinitionId(sourceDefinitionId); + + Optional authSpec = OauthModelConverter.getAuthSpec(spec); + if (authSpec.isPresent()) { + specRead.setAuthSpecification(authSpec.get()); + } + + return specRead; } public DestinationDefinitionSpecificationRead getDestinationSpecification(DestinationDefinitionIdRequestBody destinationDefinitionIdRequestBody) @@ -260,7 +269,8 @@ public DestinationDefinitionSpecificationRead getDestinationSpecification(Destin final String imageName = DockerUtils.getTaggedImageName(destination.getDockerRepository(), destination.getDockerImageTag()); final SynchronousResponse response = getConnectorSpecification(imageName); final ConnectorSpecification spec = response.getOutput(); - return new DestinationDefinitionSpecificationRead() + + DestinationDefinitionSpecificationRead specRead = new DestinationDefinitionSpecificationRead() .jobInfo(JobConverter.getSynchronousJobRead(response)) .supportedDestinationSyncModes(Enums.convertListTo(spec.getSupportedDestinationSyncModes(), DestinationSyncMode.class)) .connectionSpecification(spec.getConnectionSpecification()) @@ -268,6 +278,13 @@ public DestinationDefinitionSpecificationRead getDestinationSpecification(Destin .supportsNormalization(spec.getSupportsNormalization()) .supportsDbt(spec.getSupportsDBT()) .destinationDefinitionId(destinationDefinitionId); + + Optional authSpec = OauthModelConverter.getAuthSpec(spec); + if (authSpec.isPresent()) { + specRead.setAuthSpecification(authSpec.get()); + } + + return specRead; } public SynchronousResponse getConnectorSpecification(String dockerImage) throws IOException { @@ -352,7 +369,7 @@ public JobInfoRead cancelJob(JobIdRequestBody jobIdRequestBody) throws IOExcepti private void cancelTemporalWorkflowIfPresent(long jobId) throws IOException { var latestAttemptId = jobPersistence.getJob(jobId).getAttempts().size() - 1; // attempts ids are monotonically increasing starting from 0 and - // specific to a job id, allowing us to do this. + // specific to a job id, allowing us to do this. var workflowId = jobPersistence.getAttemptTemporalWorkflowId(jobId, latestAttemptId); if (workflowId.isPresent()) { diff --git a/docs/reference/api/generated-api-html/index.html b/docs/reference/api/generated-api-html/index.html index 882c56a17a4db..18a2b82387be5 100644 --- a/docs/reference/api/generated-api-html/index.html +++ b/docs/reference/api/generated-api-html/index.html @@ -2296,6 +2296,12 @@

Example data

"supportedDestinationSyncModes" : [ null, null ], "supportsDbt" : true, "destinationDefinitionId" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", + "authSpecification" : { + "auth_type" : "oauth2.0", + "oauth2Specification" : { + "oauthFlowInitParameters" : [ [ "oauthFlowInitParameters", "oauthFlowInitParameters" ], [ "oauthFlowInitParameters", "oauthFlowInitParameters" ] ] + } + }, "jobInfo" : { "createdAt" : 0, "configId" : "configId", @@ -4641,6 +4647,12 @@

Example data

} }, "sourceDefinitionId" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", + "authSpecification" : { + "auth_type" : "oauth2.0", + "oauth2Specification" : { + "oauthFlowInitParameters" : [ [ "oauthFlowInitParameters", "oauthFlowInitParameters" ], [ "oauthFlowInitParameters", "oauthFlowInitParameters" ] ] + } + }, "jobInfo" : { "createdAt" : 0, "configId" : "configId", @@ -6004,6 +6016,7 @@

Table of Contents

  • AttemptInfoRead -
  • AttemptRead -
  • AttemptStatus -
  • +
  • AuthSpecification -
  • CheckConnectionRead -
  • CheckOperationRead -
  • CompleteDestinationOAuthRequest -
  • @@ -6058,6 +6071,7 @@

    Table of Contents

  • Notification -
  • NotificationRead -
  • NotificationType -
  • +
  • OAuth2Specification -
  • OAuthConsentRead -
  • OperationCreate -
  • OperationIdRequestBody -
  • @@ -6171,6 +6185,16 @@

    AttemptStatus - +
    +

    AuthSpecification - Up

    +
    +
    +
    auth_type (optional)
    +
    Enum:
    +
    oauth2.0
    +
    oauth2Specification (optional)
    +
    +

    CheckConnectionRead - Up

    @@ -6410,6 +6434,7 @@

    DestinationDefinition
    destinationDefinitionId
    UUID format: uuid
    documentationUrl (optional)
    connectionSpecification (optional)
    +
    authSpecification (optional)
    jobInfo
    supportedDestinationSyncModes (optional)
    supportsDbt (optional)
    @@ -6656,6 +6681,13 @@

    NotificationType -

    +
    +

    OAuth2Specification - Up

    +
    An object containing any metadata needed to describe this connector's Oauth flow
    +
    +
    oauthFlowInitParameters (optional)
    array[array[String]] Pointers to the fields in the ConnectorSpecification which are needed to obtain the initial refresh/access tokens for the OAuth flow. Each inner array represents the path in the ConnectorSpecification of the referenced field. For example. Assume the ConnectorSpecification contains params 'app_secret', 'app_id' which are needed to get the initial refresh token. If they are not nested in the config, then the array would look like this [['app_secret'], ['app_id']] If they are nested inside, say, an object called 'auth_params' then this array would be [['auth_params', 'app_secret'], ['auth_params', 'app_id']]
    +
    +

    OAuthConsentRead - Up

    @@ -6849,6 +6881,7 @@

    SourceDefinitionSpecificat
    sourceDefinitionId
    UUID format: uuid
    documentationUrl (optional)
    connectionSpecification (optional)
    +
    authSpecification (optional)
    jobInfo