# 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}-documents

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/${opt:service}.yml):${opt:service}}
  apiVersion: v1
  apiBaseUrl: "api/${self:custom.apiVersion}"
  region: ${opt:region, self:custom.service.aws.region}
  namespace: "${self:service}-${self:custom.region}"
  serviceName: ${opt:service}
  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"
  apiDocsRoleName: "${self:custom.namespace}-api-docs-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:
  documents-index:
    handler: handler.documents_index
    role: apiDocsRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/documents
          method: get
          cors: true

  documents-audits:
    handler: handler.documents_audits
    role: apiDocsRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/documents/{id}/audits
          method: get
          cors: true
          request:
            parameters:
              paths:
                id: true

  documents-download:
    handler: handler.documents_download
    role: apiDocsRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/documents/{id}/download
          method: get
          cors: true
          request:
            parameters:
              paths:
                id: true

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

  documents-create:
    handler: handler.documents_create
    role: apiDocsRole
    events:
      - http:
          path: ${self:custom.apiBaseUrl}/documents
          method: post
          cors: true

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

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


  # TODO: Add slack command & action function

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:
    # TODO: Finish support for s3 static site? CloudFront? Or remove this?
    # siteBucket:
    #   Type: 'AWS::S3::Bucket'
    #   Properties:
    #     BucketName: ${self:custom.siteBucketName}
    #     AccessControl: PublicRead
    #     WebsiteConfiguration:
    #       IndexDocument: index.html
    #       ErrorDocument: error.html
    #   # DeletionPolicy: Retain

    # siteBucketPolicy:
    #   Type: 'AWS::S3::BucketPolicy'
    #   Properties:
    #     Bucket: ${self:custom.siteBucketName}
    #     PolicyDocument:
    #       Id: PublicS3WebsitePolicy
    #       Version: '2012-10-17'
    #       Statement:
    #         - Sid: PublicReadForGetBucketObjects
    #           Effect: Allow
    #           Principal: '*'
    #           Action: 's3:GetObject'
    #           Resource: 'arn:aws:s3:::${self:custom.siteBucketName}/*'

    # assetBucket:
    #   Type: 'AWS::S3::Bucket'
    #   Properties:
    #     BucketName: ${self:custom.assetBucketName}
    #     AccessControl: PublicRead
    #     WebsiteConfiguration:
    #       IndexDocument: index.html
    #       ErrorDocument: error.html
    #   # DeletionPolicy: Retain
    #     CorsConfiguration:
    #       CorsRules:
    #         - MaxAge: 300
    #           # ExposedHeaders: ['*']
    #           AllowedHeaders: ['*']
    #           AllowedOrigins: ['*']
    #           AllowedMethods:
    #             - HEAD
    #             - GET
    #             - PUT

    # assetBucketPolicy:
    #   Type: 'AWS::S3::BucketPolicy'
    #   Properties:
    #     Bucket: ${self:custom.assetBucketName}
    #     PolicyDocument:
    #       Id: PublicS3WebsitePolicy
    #       Version: '2012-10-17'
    #       Statement:
    #         - Sid: PublicReadForGetBucketObjects
    #           Effect: Allow
    #           Principal: '*'
    #           Action: 's3:GetObject'
    #           Resource: 'arn:aws:s3:::${self:custom.assetBucketName}/*'

    # buildBucket:
    #   Type: AWS::S3::Bucket
    #   Properties:
    #     BucketName: ${self:custom.buildBucketName}

    # secureBucket:
    #   Type: AWS::S3::Bucket
    #   Properties:
    #     BucketName: ${self:custom.secureBucketName}
    #     AccessControl: Private
    #     BucketEncryption:
    #       ServerSideEncryptionConfiguration:
    #         - ServerSideEncryptionByDefault:
    #             SSEAlgorithm: AES256
    #     CorsConfiguration:
    #       CorsRules:
    #         - MaxAge: 300
    #           # ExposedHeaders: ['*']
    #           AllowedHeaders: ['*']
    #           AllowedOrigins: ['*']
    #           AllowedMethods:
    #             - HEAD
    #             - GET
    #             - PUT

    # ecrRepository:
    #   Type: AWS::ECR::Repository
    #   Properties:
    #     RepositoryName: ${self:custom.ecrRepoName}

    # buildsTable:
    #   Type: AWS::DynamoDB::Table
    #   Properties:
    #     TableName: ${self:custom.buildsTableName}
    #     BillingMode: PAY_PER_REQUEST
    #     AttributeDefinitions:
    #       - AttributeName: id
    #         AttributeType: 'S'
    #       # - AttributeName: build_timestamp
    #       #   AttributeType: 'N'
    #     # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
    #     KeySchema:
    #       - AttributeName: id
    #         KeyType: HASH
    #       # - AttributeName: build_timestamp
    #       #   KeyType: RANGE

    # deploysTable:
    #   Type: AWS::DynamoDB::Table
    #   Properties:
    #     TableName: ${self:custom.deploysTableName}
    #     BillingMode: PAY_PER_REQUEST
    #     AttributeDefinitions:
    #       - AttributeName: id
    #         AttributeType: 'S'
    #       # - AttributeName: deploy_timestamp
    #       #   AttributeType: 'N'
    #     # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
    #     KeySchema:
    #       - AttributeName: id
    #         KeyType: HASH
    #       # - AttributeName: deploy_timestamp
    #       #   KeyType: RANGE

    # documentsTable:
    #   Type: AWS::DynamoDB::Table
    #   Properties:
    #     TableName: ${self:custom.documentsTableName}
    #     BillingMode: PAY_PER_REQUEST
    #     AttributeDefinitions:
    #       - AttributeName: id
    #         AttributeType: 'S'
    #       - AttributeName: timestamp
    #         AttributeType: 'N'
    #     KeySchema:
    #       - AttributeName: id
    #         KeyType: HASH
    #       - AttributeName: timestamp
    #         KeyType: RANGE

    # sendEmailConfigSet:
    #   Type: AWS::SES::ConfigurationSet
    #   Properties:
    #     Name: "${self:custom.sendEmailConfName}"

    # sendEmailRole:
    #   Type: AWS::IAM::Role
    #   Properties:
    #     RoleName: ${self:custom.sendEmailRoleName}
    #     AssumeRolePolicyDocument:
    #       Version: '2012-10-17'
    #       Statement:
    #         - Effect: Allow
    #           Principal:
    #             Service:
    #               - lambda.amazonaws.com
    #           Action: sts:AssumeRole
    #     Policies:
    #       - PolicyName: sendEmail
    #         PolicyDocument:
    #           Version: '2012-10-17'
    #           Statement:
    #             - Effect: "Allow"
    #               Action:
    #                 - "ses:SendEmail"
    #               Resource:
    #                 - "*"
    #             - 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}-*:*:*" ] ]  }

    apiDocsRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: ${self:custom.apiDocsRoleName}
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: notifySlack
            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"
