# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: sls-ci-${self:custom.serviceName}-ci

plugins:
  - serverless-offline
  # - serverless-webpack

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"
package:
  exclude:
    - tmp/**
    - .git/**
    - test/**
    - services/**
  include:
    - "./services/${self:custom.serviceName}.yml"

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: ${self:custom.region}
  profile: ${opt:aws-profile, self:custom.service.aws.profile}
  apiGateway:
    restApiId:
      Fn::ImportValue: '${self:custom.serviceName}-restApiId'
    restApiRootResourceId:
      Fn::ImportValue: '${self:custom.serviceName}-restApiRootResourceId'
    # restApiResources:
    #   users: { 'Fn::ImportValue': '${self:custom.serviceName}-ApiGatewayResourceUsers' }
    #   users/me:
    #     Fn::ImportValue: '${self:custom.serviceName}-ApiGatewayResourceUsersMe'
  # [HIGH] TODO: Clean this up, don't default to full access to Dynamo
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "dynamodb:*"
      Resource: "*"
  environment:
    SLS_BASE_URL: { "Fn::Join" : ["", [" https://", { "Fn::ImportValue" : "${self:custom.serviceName}-restApiId" }, ".execute-api.${self:custom.region}.amazonaws.com/${self:provider.stage}" ] ] }
    SLS_API_BASE: ${self:custom.apiBaseUrl}
    SLS_STAGE: ${self:provider.stage}
    CTX_SECURE: sec
    ECR_REPO_NAME: ${self:custom.ecrRepoName}
    SERVICE_NAME: ${self:custom.serviceName}
    # TODO: Add github secret, token
    BUILD_BUCKET_NAME: ${self:custom.buildBucketName}
    SITE_BUCKET_NAME: ${self:custom.siteBucketName}
    ASSET_BUCKET_NAME: ${self:custom.assetBucketName}
    SECURE_BUCKET_NAME: ${self:custom.secureBucketName}
    BUILDS_TABLE_NAME: ${self:custom.buildsTableName}
    DEPLOYS_TABLE_NAME: ${self:custom.deploysTableName}
    AUDITS_TABLE_NAME: ${self:custom.auditsTableName}
    DOCUMENTS_TABLE_NAME: ${self:custom.documentsTableName}
    SUBMISSIONS_TABLE_NAME: ${self:custom.submissionsTableName}
    BUILD_PROJECT_NAME: ${self:custom.buildProjectName}
    RECAPTCHA_SECRET_KEY: ${self:custom.recaptchaSecretKey}
    # TODO: Add slack token for app/commands/etc
    SLACK_WEBHOOK: ${self:custom.service.slack.webhook, ''}
    SLACK_CHANNEL: ${self:custom.service.slack.channel, ''}
    SLACK_USERNAME: ${self:custom.service.slack.username, ''}
    SLACK_ICON_EMOJI: ${self:custom.service.slack.icon_emoji, ''}
    SLACK_ICON_URL: ${self:custom.service.slack.icon_url, ''}
    SEND_EMAIL_FROM: ${self:custom.sendEmailFromAddress}
    SEND_EMAIL_CONF: ${self:custom.sendEmailConfName}
    PUBNUB_PUBLISH_KEY: ${self:custom.service.pubnub.publish_key, ''}
    PUBNUB_SUBSCRIBE_KEY: ${self:custom.service.pubnub.subscribe_key, ''}
    UPDATE_ROLE_ARN: { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ]  }
    NOTIFY_SNS_ARN: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ]  }

custom:
  serverless-offline:
    port: 5000
    prefix: dev
    stage: dev
  service: ${file(./services/${env:SERVICE_NAME}.yml):${env:SERVICE_NAME}}
  apiVersion: v1
  apiBaseUrl: "api/${self:custom.apiVersion}"
  region: ${opt:region, self:custom.service.aws.region}
  namespace: "${self:service}-${self:custom.region}"
  serviceName: ${env:SERVICE_NAME}
  ecrRepoName: ${self:custom.namespace}-repo
  buildBucketName: ${self:custom.namespace}-build-bucket
  siteBucketName: ${self:custom.namespace}-site
  assetBucketName: ${self:custom.namespace}-assets
  secureBucketName: ${self:custom.namespace}-secure
  buildsTableName: "${self:custom.namespace}-builds"
  deploysTableName: "${self:custom.namespace}-deploys"
  auditsTableName: "${self:custom.namespace}-audits"
  documentsTableName: "${self:custom.namespace}-documents"
  submissionsTableName: "${self:custom.namespace}-submissions"
  buildProjectName: "${self:custom.namespace}-build-project"
  # buildProjectSource: ${self:custom.service.source}
  buildProjectSourceType: ${self:custom.service.source.type, 'GITHUB'}
  buildProjectSourceBase: ${self:custom.service.source.base, 'https://github.com'}
  sendEmailFromAddress: ${self:custom.service.email, ''}
  stackChangeTopic: "${self:custom.namespace}-stack-change"
  buildChangeTopic: "${self:custom.namespace}-build-change"
  buildProjectRoleName: "${self:custom.namespace}-build-project-role"
  startBuildRoleName: "${self:custom.namespace}-start-build-role"
  sendEmailRoleName: "${self:custom.namespace}-send-email-role"
  sendEmailConfName: "${self:custom.namespace}-send-email-conf"
  apiCiRoleName: "${self:custom.namespace}-api-ci-role"
  notifySlackRoleName: "${self:custom.namespace}-notify-slack-role"
  stackUpdateRoleName: "${self:custom.namespace}-update-stack-role"
  serviceUpdateRoleName: "${self:custom.namespace}-update-service-role"
  recaptchaSecretKey: "${self:custom.service.recaptcha_secret_key, ''}"

functions:
  info:
    handler: handler.info
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/info
          method: get
          cors: true

  builds-index:
    handler: handler.builds_index
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/builds
          method: get
          cors: true

  builds-tags:
    handler: handler.builds_tags
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/builds/tags
          method: get
          cors: true

  builds-show:
    handler: handler.builds_show
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/builds/{id}
          method: get
          cors: true
          request:
            parameters:
              paths:
                id: true

  # builds-create:
  #   handler: handler.builds_create
  #   role: apiCiRole
  #   events:
  #     - http:
  #         path: ${self:custom.apiBaseUrl}/builds
  #         method: post
  #         cors: true

  # builds-update:
  #   handler: handler.builds_update
  #   role: apiCiRole
  #   events:
  #     - http:
  #         path: ${self:custom.apiBaseUrl}/builds/{id}
  #         method: put
  #         cors: true
  #         request:
  #           parameters:
  #             paths:
  #               id: true

  # builds-destroy:
  #   handler: handler.builds_destroy
  #   role: apiCiRole
  #   events:
  #     - http:
  #         path: ${self:custom.apiBaseUrl}/builds/{id}
  #         method: delete
  #         cors: true
  #         request:
  #           parameters:
  #             paths:
  #               id: true

  deploys-index:
    handler: handler.deploys_index
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys
          method: get
          cors: true

  deploys-stacks:
    handler: handler.deploys_stacks
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys/stacks
          method: get
          cors: true

  deploys-show:
    handler: handler.deploys_show
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys/{id}
          method: get
          cors: true
          request:
            parameters:
              paths:
                id: true

  deploys-create:
    handler: handler.deploys_create
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys
          method: post
          cors: true

  deploys-update:
    handler: handler.deploys_update
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys/{id}
          method: put
          cors: true
          request:
            parameters:
              paths:
                id: true

  deploys-destroy:
    handler: handler.deploys_destroy
    role: apiCiRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/deploys/{id}
          method: delete
          cors: true
          request:
            parameters:
              paths:
                id: true

resources:
  Conditions:
    HasTargetEcs: { "Fn::Equals" : ["${self:custom.service.target}", "ecs"] }
    HasTargetS3: { "Fn::Equals" : ["${self:custom.service.target}", "s3"] }
    # HasEmailFrom: { "Fn::Not": [ "Fn::Equals": ["${self:custom.sendEmailFromAddress}", ""] ] }
  Resources:
    apiCiRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: ${self:custom.apiCiRoleName}
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: slsApiPolicy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                # [HIGH] TODO: Refactor policies & access => min required perms
                - Effect: "Allow"
                  Action:
                    - "s3:*"
                  Resource:
                    # - 'arn:aws:s3:::${self:custom.secureBucketName}'
                    # - 'arn:aws:s3:::${self:custom.secureBucketName}/*'
                    - 'arn:aws:s3:::${self:custom.buildBucketName}'
                    - 'arn:aws:s3:::${self:custom.buildBucketName}/*'
                    - 'arn:aws:s3:::${self:custom.siteBucketName}'
                    - 'arn:aws:s3:::${self:custom.siteBucketName}/*'
                    # - 'arn:aws:s3:::${self:custom.assetBucketName}'
                    # - 'arn:aws:s3:::${self:custom.assetBucketName}/*'
                - Effect: "Allow"
                  Action:
                    - "logs:CreateLogStream"
                  Resource:
                    - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ]  }
                - Effect: "Allow"
                  Action:
                    - "logs:PutLogEvents"
                  Resource:
                    - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ]  }
                - Effect: Allow
                  Action:
                    - dynamodb:*
                  Resource:
                    - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
                    - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
                    # - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
                    # - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
                    # - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
                # - Effect: Allow
                #   Action:
                #     - codebuild:BatchGetBuilds
                #   Resource:
                #     - { "Fn::Join" : ["", ["arn:aws:codebuild:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":project/${self:custom.buildProjectName}"]]}
                - Effect: Allow
                  Action:
                    - "logs:GetLogEvents"
                  Resource:
                    - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/codebuild/${self:custom.buildProjectName}:log-stream:*"]]}
                # - Effect: "Allow"
                #   Action:
                #     - "cloudformation:UpdateStack"
                #     - "cloudformation:DescribeStacks"
                #   Resource:
                #     # - "*"
                #     # [HIGH] TODO: Why was this failing perms for example-app?
                #     - { "Fn::Join" : ["", ["arn:aws:cloudformation:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":stack/${self:custom.serviceName}-*" ] ]  }
                # - Effect: "Allow"
                #   Action:
                #     - "iam:PassRole"
                #   Resource:
                #     - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ]  }

  # Outputs:
  #   buildBucket:
  #     Description: 'buildBucket value'
  #     Value: { "Ref": "buildBucket" }
  #     Export:
  #       Name: "${self:custom.serviceName}-buildBucket"
  #   siteBucket:
  #     Description: 'siteBucket value'
  #     Value: { "Ref": "siteBucket" }
  #     Export:
  #       Name: "${self:custom.serviceName}-siteBucket"
  #   buildProject:
  #     Description: 'buildProject value'
  #     Value: { "Ref": "buildProject" }
  #     Export:
  #       Name: "${self:custom.serviceName}-buildProject"
  #   buildProjectRole:
  #     Description: 'buildProjectRole value'
  #     Value: { "Ref": "buildProjectRole" }
  #     Export:
  #       Name: "${self:custom.serviceName}-buildProjectRole"
  #   buildsTable:
  #     Description: 'buildsTable value'
  #     Value: { "Ref": "buildsTable" }
  #     Export:
  #       Name: "${self:custom.serviceName}-buildsTable"
  #   deploysTable:
  #     Description: 'deploysTable value'
  #     Value: { "Ref": "deploysTable" }
  #     Export:
  #       Name: "${self:custom.serviceName}-deploysTable"
  #   ecrRepository:
  #     Description: 'ecrRepository value'
  #     Value: { "Ref": "ecrRepository" }
  #     Export:
  #       Name: "${self:custom.serviceName}-ecrRepository"
  #   notifySlackRole:
  #     Description: 'notifySlackRole value'
  #     Value: { "Ref": "notifySlackRole" }
  #     Export:
  #       Name: "${self:custom.serviceName}-notifySlackRole"
  #   startBuildRole:
  #     Description: 'startBuildRole value'
  #     Value: { "Ref": "startBuildRole" }
  #     Export:
  #       Name: "${self:custom.serviceName}-startBuildRole"
  #   updateServiceRole:
  #     Description: 'updateServiceRole value'
  #     Value: { "Ref": "updateServiceRole" }
  #     Export:
  #       Name: "${self:custom.serviceName}-updateServiceRole"
  #   updateStackRole:
  #     Description: 'updateStackRole value'
  #     Value: { "Ref": "updateStackRole" }
  #     Export:
  #       Name: "${self:custom.serviceName}-updateStackRole"
  #   notifySnsTopic:
  #     Description: 'notifySnsTopic value'
  #     Value: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ]  }
  #     Export:
  #       Name: "${self:custom.serviceName}-notifySnsTopic"
