diff --git a/models/functions.go b/models/functions.go index a53483b5e..564e1e749 100644 --- a/models/functions.go +++ b/models/functions.go @@ -14,15 +14,18 @@ package models import "time" type FunctionsResult struct { - FunctionName string `json:"function_name"` - Description string `json:"description"` - Tags []string `json:"tags"` - Language string `json:"language"` - LastCommit time.Time `json:"last_commit"` - Link string `json:"link"` - Repository string `json:"repository"` - Branch string `json:"branch"` - Owner string `json:"owner"` + FunctionName string `json:"function_name"` + Description string `json:"description"` + Tags []string `json:"tags"` + RunTime string `json:"runtime"` + Memory int `json:"memory"` + Storgae int `json:"storgae"` + LastCommit time.Time `json:"last_commit"` + Link string `json:"link"` + Repository string `json:"repository"` + Branch string `json:"branch"` + Owner string `json:"owner"` + EnvironmentVars map[string]string `json:"environment_vars"` } type FunctionsRes struct { diff --git a/server/memphis_cloud.go b/server/memphis_cloud.go index c6f32dd0d..ae049ff57 100644 --- a/server/memphis_cloud.go +++ b/server/memphis_cloud.go @@ -2190,3 +2190,9 @@ func validateProducersCount(stationId int, tenantName string) error { func InitializeCloudFunctionRoutes(functionsHandler FunctionsHandler, functionsRoutes *gin.RouterGroup) { } + +// Integrations + +func (it IntegrationsHandler) GetSourecCodeBranches(c *gin.Context) { + c.IndentedJSON(401, "Unautorized") +} diff --git a/server/memphis_handlers_functions.go b/server/memphis_handlers_functions_cloud.go similarity index 74% rename from server/memphis_handlers_functions.go rename to server/memphis_handlers_functions_cloud.go index 8df2643a2..ed1f01f0c 100644 --- a/server/memphis_handlers_functions.go +++ b/server/memphis_handlers_functions_cloud.go @@ -36,7 +36,7 @@ func (fh FunctionsHandler) GetAllFunctions(c *gin.Context) { return } - c.JSON(200, gin.H{"scm_integrated": functionsResult.ScmIntegrated, "functions": functionsResult.Functions}) + c.IndentedJSON(200, gin.H{"scm_integrated": functionsResult.ScmIntegrated, "functions": functionsResult.Functions}) } func (fh FunctionsHandler) GetFunctions(tenantName string) (models.FunctionsRes, error) { @@ -54,7 +54,7 @@ func (fh FunctionsHandler) GetFunctions(tenantName string) (models.FunctionsRes, } func validateYamlContent(yamlMap map[string]interface{}) error { - requiredFields := []string{"function_name", "language"} + requiredFields := []string{"function_name", "runtime", "dependencies"} missingFields := make([]string, 0) for _, field := range requiredFields { if _, exists := yamlMap[field]; !exists { @@ -91,21 +91,37 @@ func GetFunctionsDetails(functionsDetails []functionDetails) ([]models.Functions } } + var environmentVarsStrings map[string]string + environmentVarsInterfaceSlice, ok := fucntionContentMap["environment_vars"].([]interface{}) + if ok { + environmentVarsStrings = make(map[string]string, len(fucntionContentMap["environment_vars"].([]interface{}))) + for _, environmentVar := range environmentVarsInterfaceSlice { + environmentVarMap := environmentVar.(map[interface{}]interface{}) + for k, v := range environmentVarMap { + if str, ok := v.(string); ok { + environmentVarsStrings[k.(string)] = str + } + } + } + } description, ok := fucntionContentMap["description"].(string) if !ok { description = "" } functionDetails := models.FunctionsResult{ - FunctionName: fucntionContentMap["function_name"].(string), - Description: description, - Tags: tagsStrings, - Language: fucntionContentMap["language"].(string), - LastCommit: *commit.Commit.Committer.Date, - Link: *fileContent.HTMLURL, - Repository: repo, - Branch: branch, - Owner: owner, + FunctionName: fucntionContentMap["function_name"].(string), + Description: description, + Tags: tagsStrings, + RunTime: fucntionContentMap["runtime"].(string), + LastCommit: *commit.Commit.Committer.Date, + Link: *fileContent.HTMLURL, + Repository: repo, + Branch: branch, + Owner: owner, + Memory: fucntionContentMap["memory"].(int), + Storgae: fucntionContentMap["storage"].(int), + EnvironmentVars: environmentVarsStrings, } functions = append(functions, functionDetails) diff --git a/server/memphis_handlers_integrations.go b/server/memphis_handlers_integrations.go index be32b56a5..3928c0173 100644 --- a/server/memphis_handlers_integrations.go +++ b/server/memphis_handlers_integrations.go @@ -188,7 +188,6 @@ func (it IntegrationsHandler) UpdateIntegration(c *gin.Context) { return } integration = githubIntegration - default: serv.Warnf("[tenant: %v]UpdateIntegration: Unsupported integration type - %v", user.TenantName, body.Name) c.AbortWithStatusJSON(SHOWABLE_ERROR_STATUS_CODE, gin.H{"message": "Unsupported integration type - " + body.Name}) @@ -198,7 +197,92 @@ func (it IntegrationsHandler) UpdateIntegration(c *gin.Context) { c.IndentedJSON(200, integration) } -func createIntegrationsKeysAndProperties(integrationType, authToken string, channelID string, pmAlert bool, svfAlert bool, disconnectAlert bool, accessKey, secretKey, bucketName, region, url, forceS3PathStyle, token, repo, branch, repoType, repoOwner string) (map[string]interface{}, map[string]bool) { +func (it IntegrationsHandler) DisconnectIntegration(c *gin.Context) { + var body models.DisconnectIntegrationSchema + ok := utils.Validate(c, &body, false, nil) + if !ok { + return + } + user, err := getUserDetailsFromMiddleware(c) + if err != nil { + serv.Errorf("DisconnectIntegration at getUserDetailsFromMiddleware: Integration %v: %v", err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + + exist, _, err := db.GetTenantByName(user.TenantName) + if err != nil { + serv.Errorf("[tenant:%v]DisconnectIntegration at GetTenantByName: %v", user.TenantName, err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + if !exist { + serv.Warnf("[tenant: %v]DisconnectIntegration : tenant %v does not exist", user.TenantName, user.TenantName) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + + integrationType := strings.ToLower(body.Name) + if integrationType == "github" { + err = deleteInstallationForAuthenticatedGithubApp(user.TenantName) + if err != nil { + if strings.Contains(err.Error(), "does not exist") { + serv.Warnf("[tenant:%v]DisconnectIntegration at deleteInstallationForAuthenticatedGithubApp: %v", user.TenantName, err.Error()) + c.AbortWithStatusJSON(SHOWABLE_ERROR_STATUS_CODE, gin.H{"message": err.Error()}) + return + } + serv.Errorf("[tenant:%v]DisconnectIntegration at deleteInstallationForAuthenticatedGithubApp: %v", user.TenantName, err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + } + + err = db.DeleteIntegration(integrationType, user.TenantName) + if err != nil { + serv.Errorf("[tenant: %v]DisconnectIntegration at db.DeleteIntegration: Integration %v: %v", user.TenantName, body.Name, err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + + integrationUpdate := models.Integration{ + Name: strings.ToLower(body.Name), + Keys: nil, + Properties: nil, + TenantName: user.TenantName, + } + + msg, err := json.Marshal(integrationUpdate) + if err != nil { + serv.Errorf("[tenant: %v]DisconnectIntegration at json.Marshal: Integration %v: %v", user.TenantName, body.Name, err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + err = serv.sendInternalAccountMsgWithReply(serv.MemphisGlobalAccount(), INTEGRATIONS_UPDATES_SUBJ, _EMPTY_, nil, msg, true) + if err != nil { + serv.Errorf("[tenant: %v]DisconnectIntegration at sendInternalAccountMsgWithReply: Integration %v: %v", user.TenantName, body.Name, err.Error()) + c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) + return + } + + switch body.Name { + case "slack": + update := models.SdkClientsUpdates{ + Type: sendNotificationType, + Update: false, + } + serv.SendUpdateToClients(update) + } + + shouldSendAnalytics, _ := shouldSendAnalytics() + if shouldSendAnalytics { + user, _ := getUserDetailsFromMiddleware(c) + analyticsParams := make(map[string]interface{}) + analytics.SendEvent(user.TenantName, user.Username, analyticsParams, "user-disconnect-integration-"+integrationType) + } + c.IndentedJSON(200, gin.H{}) +} + +func createIntegrationsKeysAndProperties(integrationType, authToken string, channelID string, pmAlert bool, svfAlert bool, disconnectAlert bool, accessKey, secretKey, bucketName, region, url, forceS3PathStyle string, githubIntegrationDetails map[string]interface{}, repo, branch, repoType, repoOwner string) (map[string]interface{}, map[string]bool) { keys := make(map[string]interface{}) properties := make(map[string]bool) switch integrationType { @@ -216,11 +300,7 @@ func createIntegrationsKeysAndProperties(integrationType, authToken string, chan keys["region"] = region keys["url"] = url case "github": - keys["token"] = token - keys["connected_repos"] = []githubRepoDetails{} - if repoOwner != "" { - keys["connected_repos"] = []githubRepoDetails{{RepoName: repo, Branch: branch, Type: repoType, RepoOwner: repoOwner}} - } + keys = getGithubKeys(githubIntegrationDetails, repoOwner, repo, branch, repoType) } return keys, properties @@ -250,14 +330,20 @@ func (it IntegrationsHandler) GetIntegrationDetails(c *gin.Context) { c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) return } + applicationName := retrieveGithubAppName() exist, integration, err := db.GetIntegration(strings.ToLower(body.Name), user.TenantName) if err != nil { serv.Errorf("[tenant: %v][user: %v]GetIntegrationDetails at db.GetIntegration: Integration %v: %v", user.TenantName, user.Username, body.Name, err.Error()) c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) return } else if !exist { - c.IndentedJSON(200, nil) - return + if body.Name == "github" { + c.IndentedJSON(200, gin.H{"application_name": applicationName}) + return + } else { + c.IndentedJSON(200, nil) + return + } } if integration.Name == "slack" && integration.Keys["auth_token"] != "" { @@ -275,46 +361,19 @@ func (it IntegrationsHandler) GetIntegrationDetails(c *gin.Context) { return } if integration.Name == "github" { - integration = sourceCodeIntegration - c.IndentedJSON(200, gin.H{"integration": integration, "repos": branchesMap}) + githubIntegration := models.Integration{} + githubIntegration.Keys = map[string]interface{}{} + githubIntegration.Name = sourceCodeIntegration.Name + githubIntegration.TenantName = sourceCodeIntegration.TenantName + githubIntegration.Keys["connected_repos"] = sourceCodeIntegration.Keys["connected_repos"] + githubIntegration.Keys["memphis_functions"] = integration.Keys["memphis_functions"] + githubIntegration.Keys["application_name"] = applicationName + c.IndentedJSON(200, gin.H{"integration": githubIntegration, "repos": branchesMap}) return } c.IndentedJSON(200, integration) } -func (it IntegrationsHandler) GetSourecCodeBranches(c *gin.Context) { - var body GetSourceCodeBranchesSchema - ok := utils.Validate(c, &body, false, nil) - if !ok { - return - } - user, err := getUserDetailsFromMiddleware(c) - if err != nil { - serv.Errorf("GetSourecCodeBranches at getUserDetailsFromMiddleware: Integration %v: %v", body.RepoName, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - integration, branches, err := getSourceCodeDetails(user.TenantName, body, "get_all_branches") - if err != nil { - if strings.Contains(err.Error(), "does not exist") { - serv.Warnf("[tenant: %v][user: %v]GetSourecCodeBranches at getSourceCodeDetails: Integration %v: %v", user.TenantName, user.Username, body.RepoName, err.Error()) - c.AbortWithStatusJSON(SHOWABLE_ERROR_STATUS_CODE, gin.H{"message": err.Error()}) - return - } - serv.Errorf("[tenant: %v][user: %v]GetSourecCodeBranches at getSourceCodeDetails: Integration %v: %v", user.TenantName, user.Username, body.RepoName, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - if integration.Name == "" { - c.IndentedJSON(200, nil) - return - } - - c.IndentedJSON(200, gin.H{"integration": integration, "branches": branches}) -} - func (it IntegrationsHandler) GetAllIntegrations(c *gin.Context) { user, err := getUserDetailsFromMiddleware(c) if err != nil { @@ -338,8 +397,8 @@ func (it IntegrationsHandler) GetAllIntegrations(c *gin.Context) { if integrations[i].Name == "s3" && integrations[i].Keys["secret_key"] != "" { integrations[i].Keys["secret_key"] = hideIntegrationSecretKey(integrations[i].Keys["secret_key"].(string)) } - if integrations[i].Name == "github" && integrations[i].Keys["token"] != "" { - integrations[i].Keys["token"] = hideIntegrationSecretKey(integrations[i].Keys["token"].(string)) + if integrations[i].Name == "github" && integrations[i].Keys["installation_id"] != "" { + delete(integrations[i].Keys, "installation_id") } } @@ -353,77 +412,6 @@ func (it IntegrationsHandler) GetAllIntegrations(c *gin.Context) { c.IndentedJSON(200, integrations) } -func (it IntegrationsHandler) DisconnectIntegration(c *gin.Context) { - var body models.DisconnectIntegrationSchema - ok := utils.Validate(c, &body, false, nil) - if !ok { - return - } - user, err := getUserDetailsFromMiddleware(c) - if err != nil { - serv.Errorf("DisconnectIntegration at getUserDetailsFromMiddleware: Integration %v: %v", err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - exist, _, err := db.GetTenantByName(user.TenantName) - if err != nil { - serv.Errorf("[tenant:%v]DisconnectIntegration at GetTenantByName: %v", user.TenantName, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - if !exist { - serv.Warnf("[tenant: %v]DisconnectIntegration : tenant %v does not exist", user.TenantName, user.TenantName) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - integrationType := strings.ToLower(body.Name) - err = db.DeleteIntegration(integrationType, user.TenantName) - if err != nil { - serv.Errorf("[tenant: %v]DisconnectIntegration at db.DeleteIntegration: Integration %v: %v", user.TenantName, body.Name, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - integrationUpdate := models.Integration{ - Name: strings.ToLower(body.Name), - Keys: nil, - Properties: nil, - TenantName: user.TenantName, - } - - msg, err := json.Marshal(integrationUpdate) - if err != nil { - serv.Errorf("[tenant: %v]DisconnectIntegration at json.Marshal: Integration %v: %v", user.TenantName, body.Name, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - err = serv.sendInternalAccountMsgWithReply(serv.MemphisGlobalAccount(), INTEGRATIONS_UPDATES_SUBJ, _EMPTY_, nil, msg, true) - if err != nil { - serv.Errorf("[tenant: %v]DisconnectIntegration at sendInternalAccountMsgWithReply: Integration %v: %v", user.TenantName, body.Name, err.Error()) - c.AbortWithStatusJSON(500, gin.H{"message": "Server error"}) - return - } - - switch body.Name { - case "slack": - update := models.SdkClientsUpdates{ - Type: sendNotificationType, - Update: false, - } - serv.SendUpdateToClients(update) - } - - shouldSendAnalytics, _ := shouldSendAnalytics() - if shouldSendAnalytics { - user, _ := getUserDetailsFromMiddleware(c) - analyticsParams := make(map[string]interface{}) - analytics.SendEvent(user.TenantName, user.Username, analyticsParams, "user-disconnect-integration-"+integrationType) - } - c.IndentedJSON(200, gin.H{}) -} - func (it IntegrationsHandler) RequestIntegration(c *gin.Context) { var body models.RequestIntegrationSchema ok := utils.Validate(c, &body, false, nil) diff --git a/server/notifications_slack.go b/server/notifications_slack.go index cc71f43b0..a5a5b3599 100644 --- a/server/notifications_slack.go +++ b/server/notifications_slack.go @@ -144,7 +144,7 @@ func (it IntegrationsHandler) getSlackIntegrationDetails(body models.CreateInteg disconnectAlert = false } - keys, properties := createIntegrationsKeysAndProperties("slack", authToken, channelID, pmAlert, svfAlert, disconnectAlert, "", "", "", "", "", "", "", "", "", "", "") + keys, properties := createIntegrationsKeysAndProperties("slack", authToken, channelID, pmAlert, svfAlert, disconnectAlert, "", "", "", "", "", "", map[string]interface{}{}, "", "", "", "") return keys, properties, 0, nil } @@ -254,7 +254,7 @@ func updateSlackIntegration(tenantName string, authToken string, channelID strin if err != nil { return slackIntegration, err } - keys, properties := createIntegrationsKeysAndProperties("slack", authToken, channelID, pmAlert, svfAlert, disconnectAlert, "", "", "", "", "", "", "", "", "", "", "") + keys, properties := createIntegrationsKeysAndProperties("slack", authToken, channelID, pmAlert, svfAlert, disconnectAlert, "", "", "", "", "", "", map[string]interface{}{}, "", "", "", "") stringMapKeys := GetKeysAsStringMap(keys) cloneKeys := copyMaps(stringMapKeys) encryptedValue, err := EncryptAES([]byte(authToken)) diff --git a/server/source_code_management.go b/server/source_code_management.go deleted file mode 100644 index 9c5ea0d0a..000000000 --- a/server/source_code_management.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2022-2023 The Memphis.dev Authors -// Licensed under the Memphis Business Source License 1.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// Changed License: [Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0), as published by the Apache Foundation. -// -// https://github.com/memphisdev/memphis/blob/master/LICENSE -// -// Additional Use Grant: You may make use of the Licensed Work (i) only as part of your own product or service, provided it is not a message broker or a message queue product or service; and (ii) provided that you do not use, provide, distribute, or make available the Licensed Work as a Service. -// A "Service" is a commercial offering, product, hosted, or managed service, that allows third parties (other than your own employees and contractors acting on your behalf) to access and/or use the Licensed Work or a substantial set of the features or functionality of the Licensed Work to third parties as a software-as-a-service, platform-as-a-service, infrastructure-as-a-service or other similar services that compete with Licensor products or services. -package server - -import ( - "errors" - "fmt" - - "github.com/memphisdev/memphis/models" - - "github.com/google/go-github/github" -) - -type GetSourceCodeBranchesSchema struct { - RepoName string `form:"repo_name" json:"repo_name" binding:"required"` - RepoOwner string `form:"repo_owner" json:"repo_owner" binding:"required"` -} - -type functionDetails struct { - Content *github.RepositoryContent `json:"content"` - Commit *github.RepositoryCommit `json:"commit"` - ContentMap map[string]interface{} `json:"content_map"` - RepoName string `json:"repo_name"` - Branch string `json:"branch"` - IntegrationName string `json:"integration_name"` - Owner string `json:"owner"` -} - -func getSourceCodeDetails(tenantName string, getAllReposSchema interface{}, actionType string) (models.Integration, interface{}, error) { - integrationRes := models.Integration{} - var allRepos interface{} - for k, sourceCodeActions := range SourceCodeManagementFunctionsMap { - switch k { - case "github": - if tenantIntegrations, ok := IntegrationsConcurrentCache.Load(tenantName); !ok { - return models.Integration{}, map[string]string{}, fmt.Errorf("failed get source code %s branches: github integration does not exist", k) - } else { - if githubIntegration, ok := tenantIntegrations["github"].(models.Integration); ok { - for a, f := range sourceCodeActions { - switch a { - case actionType: - var schema interface{} - if actionType == "get_all_repos" { - schema = getAllReposSchema.(models.GetIntegrationDetailsSchema) - } else if actionType == "get_all_branches" { - schema = getAllReposSchema.(GetSourceCodeBranchesSchema) - } - integrationRes, allRepos, err := f.(func(models.Integration, interface{}) (models.Integration, interface{}, error))(githubIntegration, schema) - if err != nil { - return models.Integration{}, map[string]string{}, err - } - return integrationRes, allRepos, nil - } - - } - } - } - default: - return models.Integration{}, map[string]string{}, errors.New("failed get source branches : unsupported integration") - } - } - return integrationRes, allRepos, nil -} - -func orderBranchesPerConnectedRepos(connectedRepos []interface{}) map[string]map[string][]string { - branchesPerRepo := map[string]map[string][]string{} - for _, connectRepo := range connectedRepos { - var connectedBranchList []string - repo := connectRepo.(map[string]interface{})["repo_name"].(string) - branch := connectRepo.(map[string]interface{})["branch"].(string) - owner := connectRepo.(map[string]interface{})["repo_owner"].(string) - if _, ok := branchesPerRepo[repo]; !ok { - connectedBranchList = append(connectedBranchList, branch) - branchesPerRepo[repo] = map[string][]string{} - branchesPerRepo[repo][owner] = connectedBranchList - } else { - connectedBranchList = append(branchesPerRepo[repo][owner], branch) - branchesPerRepo[repo][owner] = connectedBranchList - } - } - return branchesPerRepo -} - -func GetContentOfSelectedRepo(integration models.Integration, connectedRepo map[string]interface{}, contentDetails []functionDetails) ([]functionDetails, error) { - var err error - switch integration.Name { - case "github": - contentDetails, err = GetGithubContentFromConnectedRepo(integration, connectedRepo, contentDetails) - if err != nil { - return contentDetails, err - } - } - - return contentDetails, nil -} - -func getConnectedSourceCodeRepos(tenantName string) (map[string][]interface{}, bool) { - selectedReposPerSourceCodeIntegration := map[string][]interface{}{} - scmIntegrated := false - selectedRepos := []interface{}{} - if tenantIntegrations, ok := IntegrationsConcurrentCache.Load(tenantName); ok { - for k := range tenantIntegrations { - if integration, ok := tenantIntegrations[k].(models.Integration); ok { - if connectedRepos, ok := integration.Keys["connected_repos"].([]interface{}); ok { - scmIntegrated = true - for _, repo := range connectedRepos { - repository := repo.(map[string]interface{}) - repoType := repository["type"].(string) - if repoType == "functions" { - selectedRepos = append(selectedRepos, repo) - selectedReposPerSourceCodeIntegration[k] = selectedRepos - } - } - } else { - continue - } - } else { - continue - } - } - - } - return selectedReposPerSourceCodeIntegration, scmIntegrated -} - -func GetContentOfSelectedRepos(tenantName string) ([]functionDetails, bool) { - contentDetails := []functionDetails{} - connectedRepos, scmIntegrated := getConnectedSourceCodeRepos(tenantName) - var err error - for k, connectedRepoPerIntegration := range connectedRepos { - for _, connectedRepo := range connectedRepoPerIntegration { - connectedRepoRes := connectedRepo.(map[string]interface{}) - tenantIntegrations, _ := IntegrationsConcurrentCache.Load(tenantName) - integration := tenantIntegrations[k].(models.Integration) - contentDetails, err = GetContentOfSelectedRepo(integration, connectedRepoRes, contentDetails) - if err != nil { - continue - } - } - } - return contentDetails, scmIntegrated -} diff --git a/server/source_code_management_cloud.go b/server/source_code_management_cloud.go new file mode 100644 index 000000000..b20fe6f53 --- /dev/null +++ b/server/source_code_management_cloud.go @@ -0,0 +1,72 @@ +// Copyright 2022-2023 The Memphis.dev Authors +// Licensed under the Memphis Business Source License 1.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// Changed License: [Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0), as published by the Apache Foundation. +// +// https://github.com/memphisdev/memphis/blob/master/LICENSE +// +// Additional Use Grant: You may make use of the Licensed Work (i) only as part of your own product or service, provided it is not a message broker or a message queue product or service; and (ii) provided that you do not use, provide, distribute, or make available the Licensed Work as a Service. +// A "Service" is a commercial offering, product, hosted, or managed service, that allows third parties (other than your own employees and contractors acting on your behalf) to access and/or use the Licensed Work or a substantial set of the features or functionality of the Licensed Work to third parties as a software-as-a-service, platform-as-a-service, infrastructure-as-a-service or other similar services that compete with Licensor products or services. +package server + +import ( + "github.com/google/go-github/github" + "github.com/memphisdev/memphis/models" +) + +type GetSourceCodeBranchesSchema struct { + RepoName string `form:"repo_name" json:"repo_name" binding:"required"` + RepoOwner string `form:"repo_owner" json:"repo_owner" binding:"required"` +} + +type functionDetails struct { + Content *github.RepositoryContent `json:"content"` + Commit *github.RepositoryCommit `json:"commit"` + ContentMap map[string]interface{} `json:"content_map"` + RepoName string `json:"repo_name"` + Branch string `json:"branch"` + IntegrationName string `json:"integration_name"` + Owner string `json:"owner"` +} + +func getSourceCodeDetails(tenantName string, getAllReposSchema interface{}, actionType string) (models.Integration, interface{}, error) { + return models.Integration{}, map[string]string{}, nil +} + +func GetContentOfSelectedRepo(connectedRepo map[string]interface{}, contentDetails []functionDetails) ([]functionDetails, error) { + var err error + contentDetails, err = GetGithubContentFromConnectedRepo(connectedRepo, contentDetails) + if err != nil { + return contentDetails, err + } + + return contentDetails, nil +} + +func getConnectedSourceCodeRepos(tenantName string) (map[string][]interface{}, bool) { + selectedReposPerSourceCodeIntegration := map[string][]interface{}{} + scmIntegrated := false + selectedRepos := []interface{}{} + selectedRepos = append(selectedRepos, memphisFunctions) + selectedReposPerSourceCodeIntegration["memphis_functions"] = selectedRepos + + return selectedReposPerSourceCodeIntegration, scmIntegrated +} + +func GetContentOfSelectedRepos(tenantName string) ([]functionDetails, bool) { + contentDetails := []functionDetails{} + connectedRepos, scmIntegrated := getConnectedSourceCodeRepos(tenantName) + var err error + for _, connectedRepoPerIntegration := range connectedRepos { + for _, connectedRepo := range connectedRepoPerIntegration { + connectedRepoRes := connectedRepo.(map[string]interface{}) + contentDetails, err = GetContentOfSelectedRepo(connectedRepoRes, contentDetails) + if err != nil { + continue + } + } + } + return contentDetails, scmIntegrated +} diff --git a/server/source_code_management_github.go b/server/source_code_management_github.go deleted file mode 100644 index 3b18bd1c7..000000000 --- a/server/source_code_management_github.go +++ /dev/null @@ -1,499 +0,0 @@ -package server - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/memphisdev/memphis/db" - "github.com/memphisdev/memphis/models" - - "github.com/google/go-github/github" - "golang.org/x/oauth2" - "gopkg.in/yaml.v2" -) - -type githubRepoDetails struct { - RepoName string `json:"repo_name"` - Branch string `json:"branch"` - Type string `json:"type"` - RepoOwner string `json:"repo_owner"` -} - -func cacheDetailsGithub(keys map[string]interface{}, properties map[string]bool, tenantName string) { - githubIntegration := models.Integration{} - githubIntegration.Keys = make(map[string]interface{}) - githubIntegration.Properties = make(map[string]bool) - if keys == nil { - deleteIntegrationFromTenant(tenantName, "github", IntegrationsConcurrentCache) - return - } - - githubIntegration.Keys = keys - githubIntegration.Name = "github" - githubIntegration.TenantName = tenantName - - if _, ok := IntegrationsConcurrentCache.Load(tenantName); !ok { - IntegrationsConcurrentCache.Add(tenantName, map[string]interface{}{"github": githubIntegration}) - } else { - err := addIntegrationToTenant(tenantName, "github", IntegrationsConcurrentCache, githubIntegration) - if err != nil { - serv.Errorf("cacheDetailsGithub: %s ", err.Error()) - return - } - } -} - -func createGithubIntegration(tenantName string, keys map[string]interface{}, properties map[string]bool) (models.Integration, error) { - exist, githubIntegration, err := db.GetIntegration("github", tenantName) - if err != nil { - return models.Integration{}, err - } else if !exist { - integrationRes, insertErr := db.InsertNewIntegration(tenantName, "github", keys, properties) - if insertErr != nil { - if strings.Contains(insertErr.Error(), "already exists") { - return models.Integration{}, errors.New("github integration already exists") - } else { - return models.Integration{}, insertErr - } - } - githubIntegration = integrationRes - integrationToUpdate := models.CreateIntegration{ - Name: "github", - Keys: keys, - Properties: properties, - TenantName: tenantName, - } - msg, err := json.Marshal(integrationToUpdate) - if err != nil { - return models.Integration{}, err - } - err = serv.sendInternalAccountMsgWithReply(serv.MemphisGlobalAccount(), INTEGRATIONS_UPDATES_SUBJ, _EMPTY_, nil, msg, true) - if err != nil { - return models.Integration{}, err - } - githubIntegration.Keys["token"] = hideIntegrationSecretKey(keys["token"].(string)) - return githubIntegration, nil - } - return models.Integration{}, errors.New("github integration already exists") -} - -func (it IntegrationsHandler) handleCreateGithubIntegration(tenantName string, keys map[string]interface{}) (models.Integration, int, error) { - statusCode, keys, err := it.handleGithubIntegration(tenantName, keys) - if err != nil { - return models.Integration{}, statusCode, err - } - - keys, properties := createIntegrationsKeysAndProperties("github", "", "", false, false, false, "", "", "", "", "", "", keys["token"].(string), "", "", "", "") - githubIntegration, err := createGithubIntegration(tenantName, keys, properties) - if err != nil { - if strings.Contains(err.Error(), "already exists") { - return models.Integration{}, SHOWABLE_ERROR_STATUS_CODE, err - } - return models.Integration{}, 500, err - } - return githubIntegration, statusCode, nil -} - -func (it IntegrationsHandler) handleGithubIntegration(tenantName string, keys map[string]interface{}) (int, map[string]interface{}, error) { - statusCode := 500 - if _, ok := keys["token"]; !ok { - keys["token"] = "" - } - if keys["token"] == "" { - if tenantInetgrations, ok := IntegrationsConcurrentCache.Load(tenantName); ok { - if githubIntegrationFromCache, ok := tenantInetgrations["github"].(models.Integration); ok { - keys["token"] = githubIntegrationFromCache.Keys["token"].(string) - } - if !ok || keys["token"] == "" { - exist, integrationFromDb, err := db.GetIntegration("github", tenantName) - if err != nil { - return 500, map[string]interface{}{}, err - } - if !exist { - statusCode = SHOWABLE_ERROR_STATUS_CODE - return SHOWABLE_ERROR_STATUS_CODE, map[string]interface{}{}, errors.New("github integration does not exist") - } - keys["token"] = integrationFromDb.Keys["token"] - } - } - } else { - encryptedValue, err := EncryptAES([]byte(keys["token"].(string))) - if err != nil { - return 500, map[string]interface{}{}, err - } - keys["token"] = encryptedValue - } - err := testGithubAccessToken(keys["token"].(string)) - if err != nil { - if strings.Contains(err.Error(), "access token is invalid") { - return SHOWABLE_ERROR_STATUS_CODE, map[string]interface{}{}, err - } - return 500, map[string]interface{}{}, err - } - return statusCode, keys, nil -} - -func (it IntegrationsHandler) handleUpdateGithubIntegration(user models.User, body models.CreateIntegrationSchema) (models.Integration, int, error) { - statusCode, keys, err := it.handleGithubIntegration(user.TenantName, body.Keys) - if err != nil { - return models.Integration{}, statusCode, err - } - githubIntegration, err := updateGithubIntegration(user, keys, map[string]bool{}) - if err != nil { - if strings.Contains(err.Error(), "does not exist") || strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "access token is invalid") { - return githubIntegration, SHOWABLE_ERROR_STATUS_CODE, err - } - return githubIntegration, 500, err - } - return githubIntegration, statusCode, nil -} - -func updateGithubIntegration(user models.User, keys map[string]interface{}, properties map[string]bool) (models.Integration, error) { - if tenantInetgrations, ok := IntegrationsConcurrentCache.Load(user.TenantName); ok { - if _, ok = tenantInetgrations["github"].(models.Integration); !ok { - return models.Integration{}, fmt.Errorf("github integration does not exist") - } - } else if !ok { - return models.Integration{}, fmt.Errorf("github integration does not exist") - } - - client, err := getGithubClient(keys["token"].(string)) - if err != nil { - return models.Integration{}, err - } - - updateIntegration := map[string]interface{}{} - updateIntegration["token"] = keys["token"].(string) - for _, key := range keys["connected_repos"].([]interface{}) { - connectedRepoDetails := key.(map[string]interface{}) - var repoOwner string - repoOwnerStr, ok := connectedRepoDetails["repo_owner"].(string) - if !ok { - repoOwnerInterface, ok := connectedRepoDetails["repo_owner"].([]interface{}) - if ok { - for _, owner := range repoOwnerInterface { - repoOwner = owner.(string) - } - } else { - userDetails, _, err := client.Users.Get(context.Background(), "") - if err != nil { - return models.Integration{}, err - } - repoOwner = userDetails.GetLogin() - } - } else { - repoOwner = repoOwnerStr - } - - _, _, err = client.Repositories.Get(context.Background(), repoOwner, connectedRepoDetails["repo_name"].(string)) - if err != nil { - if strings.Contains(err.Error(), "Not Found") { - updateIntegration["connected_repos"] = []githubRepoDetails{} - continue - } else { - return models.Integration{}, fmt.Errorf("repository %s not found", connectedRepoDetails["repo_name"].(string)) - } - } - - githubDetails := githubRepoDetails{ - RepoName: connectedRepoDetails["repo_name"].(string), - Branch: connectedRepoDetails["branch"].(string), - Type: connectedRepoDetails["type"].(string), - RepoOwner: repoOwner, - } - - if connectedRepositories, ok := updateIntegration["connected_repos"].([]githubRepoDetails); !ok { - updateIntegration["connected_repos"] = []githubRepoDetails{} - updateIntegration["connected_repos"] = append(connectedRepositories, githubDetails) - } else { - updateIntegration["connected_repos"] = append(connectedRepositories, githubDetails) - } - } - - if len(keys["connected_repos"].([]interface{})) == 0 { - updateIntegration["connected_repos"] = []githubRepoDetails{} - } - - githubIntegration, err := db.UpdateIntegration(user.TenantName, "github", updateIntegration, properties) - if err != nil { - return models.Integration{}, err - } - - integrationToUpdate := models.CreateIntegration{ - Name: githubIntegration.Name, - Keys: githubIntegration.Keys, - Properties: githubIntegration.Properties, - TenantName: githubIntegration.TenantName, - } - - msg, err := json.Marshal(integrationToUpdate) - if err != nil { - return models.Integration{}, err - } - err = serv.sendInternalAccountMsgWithReply(serv.MemphisGlobalAccount(), INTEGRATIONS_UPDATES_SUBJ, _EMPTY_, nil, msg, true) - if err != nil { - return models.Integration{}, err - } - - githubIntegration.Keys["token"] = hideIntegrationSecretKey(githubIntegration.Keys["token"].(string)) - return githubIntegration, nil -} - -func testGithubAccessToken(token string) error { - ctx := context.Background() - client, err := getGithubClient(token) - if err != nil { - return err - - } - // If the request was successful, the token is valid - _, _, err = client.Users.Get(ctx, "") - if err != nil { - if strings.Contains(err.Error(), "Bad credentials") { - return fmt.Errorf("The github access token is invalid") - } - return err - - } - return nil -} - -func getGithubClient(token string) (*github.Client, error) { - key := getAESKey() - decryptedValue, err := DecryptAES(key, token) - if err != nil { - return &github.Client{}, err - } - - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: decryptedValue}, - ) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) - - return client, nil -} - -func (s *Server) getGithubRepositories(integration models.Integration, body interface{}) (models.Integration, interface{}, error) { - ctx := context.Background() - opt := &github.RepositoryListOptions{ - ListOptions: github.ListOptions{PerPage: 100}, - } - - client, err := getGithubClient(integration.Keys["token"].(string)) - if err != nil { - return models.Integration{}, map[string]string{}, err - } - repositoriesMap := make(map[string][]string) - - for { - repos, resp, err := client.Repositories.List(ctx, "", opt) - if err != nil { - return models.Integration{}, map[string]string{}, err - } - - for _, repo := range repos { - repoOwner := repo.GetOwner().GetLogin() - repoName := repo.GetName() - if _, exists := repositoriesMap[repoName]; exists { - repositoriesMap[repoName] = append(repositoriesMap[repoName], repoOwner) - } else { - repositoriesMap[repoName] = []string{repoOwner} - } - } - - // Check if there are more pages - if resp.NextPage == 0 { - break - } - // Set the next page option to fetch the next page of results - opt.Page = resp.NextPage - } - - stringMapKeys := GetKeysAsStringMap(integration.Keys) - cloneKeys := copyMaps(stringMapKeys) - interfaceMapKeys := copyStringMapToInterfaceMap(cloneKeys) - interfaceMapKeys["connected_repos"] = integration.Keys["connected_repos"] - interfaceMapKeys["token"] = hideIntegrationSecretKey(interfaceMapKeys["token"].(string)) - integrationRes := models.Integration{ - Name: integration.Name, - Keys: interfaceMapKeys, - Properties: integration.Properties, - TenantName: integration.TenantName, - } - - return integrationRes, repositoriesMap, nil -} - -func (s *Server) getGithubBranches(integration models.Integration, body interface{}) (models.Integration, interface{}, error) { - branchesMap := make(map[string][]string) - repoOwner := strings.ToLower(body.(GetSourceCodeBranchesSchema).RepoOwner) - repoName := body.(GetSourceCodeBranchesSchema).RepoName - - token := integration.Keys["token"].(string) - connectedRepos := integration.Keys["connected_repos"].([]interface{}) - - client, err := getGithubClient(token) - if err != nil { - return models.Integration{}, map[string][]string{}, err - } - - opts := &github.ListOptions{PerPage: 100} - var branches []*github.Branch - var resp *github.Response - for { - branches, resp, err = client.Repositories.ListBranches(context.Background(), repoOwner, repoName, opts) - if err != nil { - if strings.Contains(err.Error(), "Not Found") { - return models.Integration{}, map[string][]string{}, fmt.Errorf("The repository %s does not exist", repoName) - } - return models.Integration{}, map[string][]string{}, err - } - - if resp.NextPage == 0 { - // No more pages - break - } - opts.Page = resp.NextPage - } - - // in case when connectedRepos holds multiple branches of the same repo - branchesPerRepo := orderBranchesPerConnectedRepos(connectedRepos) - - branchInfoList := []string{} - for _, branch := range branches { - isRepoExists := false - if len(branchesPerRepo) == 0 { - isRepoExists = true - branchInfoList = append(branchInfoList, *branch.Name) - } - for repo, branches := range branchesPerRepo { - if repo == repoName { - for owner := range branches { - if owner == repoOwner { - isRepoExists = true - isBranchExists := containsElement(branches[owner], *branch.Name) - if !isBranchExists { - branchInfoList = append(branchInfoList, *branch.Name) - } - } - } - } - } - if !isRepoExists { - branchInfoList = append(branchInfoList, *branch.Name) - } - } - - if len(branchInfoList) > 0 { - branchesMap[repoName] = branchInfoList - } - - stringMapKeys := GetKeysAsStringMap(integration.Keys) - cloneKeys := copyMaps(stringMapKeys) - interfaceMapKeys := copyStringMapToInterfaceMap(cloneKeys) - interfaceMapKeys["connected_repos"] = integration.Keys["connected_repos"] - interfaceMapKeys["token"] = hideIntegrationSecretKey(interfaceMapKeys["token"].(string)) - integrationRes := models.Integration{ - Name: integration.Name, - Keys: interfaceMapKeys, - Properties: integration.Properties, - TenantName: integration.TenantName, - } - - return integrationRes, branchesMap, nil -} - -func containsElement(arr []string, val string) bool { - for _, item := range arr { - if item == val { - return true - } - } - return false -} - -func GetGithubContentFromConnectedRepo(githubIntegration models.Integration, connectedRepo map[string]interface{}, functionsDetails []functionDetails) ([]functionDetails, error) { - token := githubIntegration.Keys["token"].(string) - branch := connectedRepo["branch"].(string) - repo := connectedRepo["repo_name"].(string) - owner := connectedRepo["repo_owner"].(string) - - client, err := getGithubClient(token) - if err != nil { - return []functionDetails{}, err - } - - _, repoContent, _, err := client.Repositories.GetContents(context.Background(), owner, repo, "", nil) - if err != nil { - return functionsDetails, err - } - - for _, directoryContent := range repoContent { - if directoryContent.GetType() == "dir" { - _, filesContent, _, err := client.Repositories.GetContents(context.Background(), owner, repo, *directoryContent.Path, nil) - if err != nil { - continue - } - - isValidFileYaml := false - for _, fileContent := range filesContent { - var content *github.RepositoryContent - var commit *github.RepositoryCommit - var contentMap map[string]interface{} - if *fileContent.Type == "file" && *fileContent.Name == "memphis.yaml" { - content, _, _, err = client.Repositories.GetContents(context.Background(), owner, repo, *fileContent.Path, nil) - if err != nil { - continue - } - - decodedContent, err := base64.StdEncoding.DecodeString(*content.Content) - if err != nil { - continue - } - - err = yaml.Unmarshal(decodedContent, &contentMap) - if err != nil { - continue - } - - err = validateYamlContent(contentMap) - if err != nil { - isValidFileYaml = false - continue - } - isValidFileYaml = true - - commit, _, err = client.Repositories.GetCommit(context.Background(), owner, repo, branch) - if err != nil { - continue - } - - if isValidFileYaml { - fileDetails := functionDetails{ - Content: content, - Commit: commit, - ContentMap: contentMap, - RepoName: repo, - Branch: branch, - IntegrationName: githubIntegration.Name, - Owner: owner, - } - functionsDetails = append(functionsDetails, fileDetails) - break - } - } - } - if !isValidFileYaml { - continue - } - } - } - - return functionsDetails, nil -} diff --git a/server/source_code_management_github_cloud.go b/server/source_code_management_github_cloud.go new file mode 100644 index 000000000..d316516a9 --- /dev/null +++ b/server/source_code_management_github_cloud.go @@ -0,0 +1,159 @@ +package server + +import ( + "context" + "encoding/base64" + + "github.com/memphisdev/memphis/models" + "gopkg.in/yaml.v2" + + "github.com/google/go-github/github" +) + +const ( + memphisDevFunctionsRepoName = "memphis-dev-functions" + memphisDevFunctionsOwnerName = "memphisdev" +) + +var memphisFunctions = map[string]interface{}{ + "repo_name": memphisDevFunctionsRepoName, + "branch": "master", + "type": "functions", + "repo_owner": memphisDevFunctionsOwnerName, +} + +type githubRepoDetails struct { + RepoName string `json:"repo_name"` + Branch string `json:"branch"` + Type string `json:"type"` + RepoOwner string `json:"repo_owner"` +} + +func (it IntegrationsHandler) handleCreateGithubIntegration(tenantName string, keys map[string]interface{}) (models.Integration, int, error) { + return models.Integration{}, 0, nil +} + +func (it IntegrationsHandler) handleUpdateGithubIntegration(user models.User, body models.CreateIntegrationSchema) (models.Integration, int, error) { + return models.Integration{}, 0, nil + +} +func cacheDetailsGithub(keys map[string]interface{}, properties map[string]bool, tenantName string) { + return +} + +func getGithubClientWithoutAccessToken() *github.Client { + client := github.NewClient(nil) + return client +} + +func (s *Server) getGithubRepositories(integration models.Integration, body interface{}) (models.Integration, interface{}, error) { + return models.Integration{}, nil, nil +} + +func (s *Server) getGithubBranches(integration models.Integration, body interface{}) (models.Integration, interface{}, error) { + return models.Integration{}, nil, nil +} + +func containsElement(arr []string, val string) bool { + for _, item := range arr { + if item == val { + return true + } + } + return false +} + +func GetGithubContentFromConnectedRepo(connectedRepo map[string]interface{}, functionsDetails []functionDetails) ([]functionDetails, error) { + branch := connectedRepo["branch"].(string) + repo := connectedRepo["repo_name"].(string) + owner := connectedRepo["repo_owner"].(string) + + var client *github.Client + var err error + client = getGithubClientWithoutAccessToken() + _, repoContent, _, err := client.Repositories.GetContents(context.Background(), owner, repo, "", nil) + if err != nil { + return functionsDetails, err + } + + for _, directoryContent := range repoContent { + if directoryContent.GetType() == "dir" { + _, filesContent, _, err := client.Repositories.GetContents(context.Background(), owner, repo, *directoryContent.Path, nil) + if err != nil { + continue + } + + isValidFileYaml := false + for _, fileContent := range filesContent { + var content *github.RepositoryContent + var commit *github.RepositoryCommit + var contentMap map[string]interface{} + if *fileContent.Type == "file" && *fileContent.Name == "memphis.yaml" { + content, _, _, err = client.Repositories.GetContents(context.Background(), owner, repo, *fileContent.Path, nil) + if err != nil { + continue + } + + decodedContent, err := base64.StdEncoding.DecodeString(*content.Content) + if err != nil { + continue + } + + err = yaml.Unmarshal(decodedContent, &contentMap) + if err != nil { + continue + } + + if _, ok := contentMap["memory"]; !ok || contentMap["memory"] == "" { + contentMap["memory"] = int64(128) * 1024 * 1024 + } + + if _, ok := contentMap["storage"]; !ok || contentMap["storage"] == "" { + contentMap["storage"] = int64(512) * 1024 * 1024 + } + + err = validateYamlContent(contentMap) + if err != nil { + isValidFileYaml = false + continue + } + isValidFileYaml = true + + commit, _, err = client.Repositories.GetCommit(context.Background(), owner, repo, branch) + if err != nil { + continue + } + + if isValidFileYaml { + fileDetails := functionDetails{ + Content: content, + Commit: commit, + ContentMap: contentMap, + RepoName: repo, + Branch: branch, + Owner: owner, + } + functionsDetails = append(functionsDetails, fileDetails) + break + } + } + } + if !isValidFileYaml { + continue + } + } + } + + return functionsDetails, nil +} + +func deleteInstallationForAuthenticatedGithubApp(tenantName string) error { + return nil +} +func getGithubKeys(githubIntegrationDetails map[string]interface{}, repoOwner, repo, branch, repoType string) map[string]interface{} { + return map[string]interface{}{} +} + +func retrieveGithubAppName() string { + return "" +} diff --git a/server/storage_s3.go b/server/storage_s3.go index 5fdddf104..80bd457f2 100644 --- a/server/storage_s3.go +++ b/server/storage_s3.go @@ -81,7 +81,7 @@ func (it IntegrationsHandler) handleCreateS3Integration(tenantName string, keys return models.Integration{}, statusCode, err } - keysMap, properties := createIntegrationsKeysAndProperties("s3", "", "", false, false, false, keys["access_key"].(string), keys["secret_key"].(string), keys["bucket_name"].(string), keys["region"].(string), keys["url"].(string), keys["s3_path_style"].(string), "", "", "", "", "") + keysMap, properties := createIntegrationsKeysAndProperties("s3", "", "", false, false, false, keys["access_key"].(string), keys["secret_key"].(string), keys["bucket_name"].(string), keys["region"].(string), keys["url"].(string), keys["s3_path_style"].(string), map[string]interface{}{}, "", "", "", "") s3Integration, err := createS3Integration(tenantName, keysMap, properties) if err != nil { if strings.Contains(err.Error(), "already exists") { @@ -99,7 +99,7 @@ func (it IntegrationsHandler) handleUpdateS3Integration(tenantName string, body return models.Integration{}, statusCode, err } integrationType := strings.ToLower(body.Name) - keysMap, properties := createIntegrationsKeysAndProperties(integrationType, "", "", false, false, false, keys["access_key"].(string), keys["secret_key"].(string), keys["bucket_name"].(string), keys["region"].(string), keys["url"].(string), keys["s3_path_style"].(string), "", "", "", "", "") + keysMap, properties := createIntegrationsKeysAndProperties(integrationType, "", "", false, false, false, keys["access_key"].(string), keys["secret_key"].(string), keys["bucket_name"].(string), keys["region"].(string), keys["url"].(string), keys["s3_path_style"].(string), map[string]interface{}{}, "", "", "", "") s3Integration, err := updateS3Integration(tenantName, keysMap, properties) if err != nil { return s3Integration, 500, err diff --git a/ui_src/src/components/deleteItemsModal/index.js b/ui_src/src/components/deleteItemsModal/index.js index 4077d8f86..edbbd3623 100644 --- a/ui_src/src/components/deleteItemsModal/index.js +++ b/ui_src/src/components/deleteItemsModal/index.js @@ -21,7 +21,7 @@ const DeleteItemsModal = ({ title, desc, handleDeleteSelected, buttontxt, textTo useEffect(() => { const keyDownHandler = (event) => { - if (event.key === 'Enter' && confirm === (textToConfirm || 'permanently delete')) { + if (event.key === 'Enter' && confirm === (textToConfirm || 'delete')) { handleDeleteSelected(); } }; @@ -37,10 +37,10 @@ const DeleteItemsModal = ({ title, desc, handleDeleteSelected, buttontxt, textTo
{desc}
- Please type {textToConfirm || 'permanently delete'} to confirm. + Please type {textToConfirm || 'delete'} to confirm.
handleDeleteSelected()} /> diff --git a/ui_src/src/components/getStartedItem/index.js b/ui_src/src/components/getStartedItem/index.js index 34168097e..20ab4f7a2 100644 --- a/ui_src/src/components/getStartedItem/index.js +++ b/ui_src/src/components/getStartedItem/index.js @@ -15,10 +15,8 @@ import './style.scss'; import React, { useContext } from 'react'; import { GetStartedStoreContext } from '../../domain/overview/getStarted'; -import bgGetStartedBottom from '../../assets/images/bgGetStartedBottom.svg'; import { ReactComponent as BgGetStartedBottomIcon } from '../../assets/images/bgGetStartedBottom.svg'; import { CONNECT_APP_VIDEO, CONNECT_CLI_VIDEO } from '../../config'; -import bgGetStarted from '../../assets/images/bgGetStarted.svg'; import { ReactComponent as BgGetStartedIcon } from '../../assets/images/bgGetStarted.svg'; import ConnectBG from '../../assets/images/connectBG.webp'; import InstallingBG from '../../assets/images/installingBG.webp'; diff --git a/ui_src/src/components/getStartedItem/style.scss b/ui_src/src/components/getStartedItem/style.scss index e9dd19486..18e559c96 100644 --- a/ui_src/src/components/getStartedItem/style.scss +++ b/ui_src/src/components/getStartedItem/style.scss @@ -5,7 +5,7 @@ position: relative; height: 100%; - .get-started-bg-img { + .get-started-bg { height: 400px; position: absolute; right: 0; diff --git a/ui_src/src/const/integrationList.js b/ui_src/src/const/integrationList.js index c25a62375..558e641e0 100644 --- a/ui_src/src/const/integrationList.js +++ b/ui_src/src/const/integrationList.js @@ -61,11 +61,11 @@ export const CATEGORY_LIST = { 'Change-Data-Capture': { name: 'Change-Data-Capture', color: ColorPalette[11] + }, + SourceCode: { + name: 'Source Code', + color: ColorPalette[6] } - // SourceCode: { - // name: 'Source Code', - // color: ColorPalette[6] - // } }; export const REGIONS_OPTIONS = [ @@ -276,34 +276,6 @@ export const INTEGRATION_LIST = {GitHub
- // by memphis - //Description
- // - // GitHub is an open source code repository and collaborative software development platform. Use GitHub repositories to manage your Schemaverse schemas - // and Functions source code. - // - //GitHub
+ by memphis +Description
+ + GitHub is an open source code repository and collaborative software development platform. Use GitHub repositories to manage your Schemaverse schemas + and Functions source code. + +0&&void 0!==arguments[0]&&arguments[0],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=null;Nt&&yn&&yn[0]&&yn[1]&&A.isAfter(yn[1],yn[0])&&(r=yn);var o=O;if(O&&"object"===(0,U.Z)(O)&&O.defaultValue){var a=O.defaultValue;o=(0,I.Z)((0,I.Z)({},O),{},{defaultValue:Ze(a,Ge)||void 0})}var s=null;return F&&(s=function(e,t){return F(e,t,{range:Ge?"end":"start"})}),i.createElement(ze.Provider,{value:{inRange:!0,panelPosition:t,rangedValue:pn||pt,hoverRangedValue:r}},i.createElement(ut,(0,c.Z)({},e,n,{dateRender:s,showTime:o,mode:At[Ge],generateConfig:A,style:void 0,direction:ye,disabledDate:0===Ge?It:Ot,disabledTime:function(e){return!!B&&B(e,0===Ge?"start":"end")},className:v()((0,f.Z)({},"".concat(u,"-panel-focused"),0===Ge?!Zn:!zn)),value:Ze(pt,Ge),locale:w,tabIndex:-1,onPanelChange:function(e,n){0===Ge&&xn(!0),1===Ge&&On(!0),_t(je(At,n,Ge),je(pt,e,Ge));var r=e;"right"===t&&At[Ge]===n&&(r=Ee(r,n,A,-1)),ot(r,Ge)},onOk:null,onSelect:void 0,onChange:void 0,defaultValue:Ze(pt,0===Ge?1:0)})))}var qn=0,Gn=0;if(Ge&&Re.current&&Fe.current&&Te.current){qn=Re.current.offsetWidth+Fe.current.offsetWidth;var Kn=We.current.offsetLeft>qn?We.current.offsetLeft-qn:We.current.offsetLeft;Te.current.offsetWidth&&We.current.offsetWidth&&qn>Te.current.offsetWidth-We.current.offsetWidth-("rtl"===ye?0:Kn)&&(Gn=qn)}var Xn="rtl"===ye?{right:qn}:{left:qn};var Yn,Jn,$n=i.createElement("div",{className:v()("".concat(u,"-range-wrapper"),"".concat(u,"-").concat(S,"-range-wrapper")),style:{minWidth:Rt}},i.createElement("div",{ref:We,className:"".concat(u,"-range-arrow"),style:Xn}),function(){var e,t=at(u,At[Ge],$),n=st({prefixCls:u,components:ge,needConfirmButton:De,okDisabled:!Ze(pt,Ge)||R&&R(pt[Ge]),locale:w,rangeList:Un,onOk:function(){Ze(pt,Ge)&&(Ht(pt,Ge),pe&&pe(pt))}});if("time"===S||O)e=Qn();else{var r=it(Ge),o=Ee(r,S,A),a=At[Ge]===S,s=Qn(!!a&&"left",{pickerValue:r,onPickerValueChange:function(e){ot(e,Ge)}}),c=Qn("right",{pickerValue:o,onPickerValueChange:function(e){ot(Ee(e,S,A,-1),Ge)}});e="rtl"===ye?i.createElement(i.Fragment,null,c,a&&s):i.createElement(i.Fragment,null,s,a&&c)}var l=i.createElement(i.Fragment,null,i.createElement("div",{className:"".concat(u,"-panels")},e),(t||n)&&i.createElement("div",{className:"".concat(u,"-footer")},t,n));return V&&(l=V(l)),i.createElement("div",{className:"".concat(u,"-panel-container"),style:{marginLeft:Gn},ref:Te,onMouseDown:function(e){e.preventDefault()}},l)}());G&&(Yn=i.createElement("span",{className:"".concat(u,"-suffix")},G)),Q&&(Ze(et,0)&&!Ye[0]||Ze(et,1)&&!Ye[1])&&(Jn=i.createElement("span",{onMouseDown:function(e){e.preventDefault(),e.stopPropagation()},onMouseUp:function(e){e.preventDefault(),e.stopPropagation();var t=et;Ye[0]||(t=je(t,null,0)),Ye[1]||(t=je(t,null,1)),Ht(t,null),Vt(!1,Ge)},className:"".concat(u,"-clear")},K||i.createElement("span",{className:"".concat(u,"-clear-btn")})));var er={size:ce(S,Ue[0],A)},tr=0,nr=0;Re.current&&Be.current&&Fe.current&&(0===Ge?nr=Re.current.offsetWidth:(tr=qn,nr=Be.current.offsetWidth));var rr="rtl"===ye?{right:tr}:{left:tr};return i.createElement(q.Provider,{value:{operationRef:Xe,hideHeader:"time"===S,onDateMouseEnter:function(e){bn(je(pt,e,Ge)),0===Ge?kn(e):In(e)},onDateMouseLeave:function(){bn(je(pt,null,Ge)),0===Ge?xn():On()},hideRanges:!0,onSelect:function(e,t){var n=je(pt,e,Ge);"submit"===t||"key"!==t&&!De?(Ht(n,Ge),0===Ge?xn():On()):vt(n)},open:Nt}},i.createElement(dt,{visible:Nt,popupElement:$n,popupStyle:p,prefixCls:u,dropdownClassName:g,dropdownAlign:y,getPopupContainer:b,transitionName:m,range:!0,direction:ye},i.createElement("div",(0,c.Z)({ref:Ne,className:v()(u,"".concat(u,"-range"),h,(t={},(0,f.Z)(t,"".concat(u,"-disabled"),Ye[0]&&Ye[1]),(0,f.Z)(t,"".concat(u,"-focused"),0===Ge?Mn:Vn),(0,f.Z)(t,"".concat(u,"-rtl"),"rtl"===ye),t)),style:d,onClick:function(e){fe&&fe(e),Nt||Ve.current.contains(e.target)||He.current.contains(e.target)||(Ye[0]?Ye[1]||zt(1):zt(0))},onMouseEnter:le,onMouseLeave:de,onMouseDown:function(e){ae&&ae(e),!Nt||!Mn&&!Vn||Ve.current.contains(e.target)||He.current.contains(e.target)||e.preventDefault()},onMouseUp:se},Me(e)),i.createElement("div",{className:v()("".concat(u,"-input"),(n={},(0,f.Z)(n,"".concat(u,"-input-active"),0===Ge),(0,f.Z)(n,"".concat(u,"-input-placeholder"),!!Cn),n)),ref:Re},i.createElement("input",(0,c.Z)({id:l,disabled:Ye[0],readOnly:Y||"function"===typeof Ue[0]||!Zn,value:Cn||rn,onChange:function(e){on(e.target.value)},autoFocus:k,placeholder:Ze(C,0)||"",ref:Ve},Tn,er,{autoComplete:Oe}))),i.createElement("div",{className:"".concat(u,"-range-separator"),ref:Fe},N),i.createElement("div",{className:v()("".concat(u,"-input"),(o={},(0,f.Z)(o,"".concat(u,"-input-active"),1===Ge),(0,f.Z)(o,"".concat(u,"-input-placeholder"),!!Sn),o)),ref:Be},i.createElement("input",(0,c.Z)({disabled:Ye[1],readOnly:Y||"function"===typeof Ue[0]||!zn,value:Sn||cn,onChange:function(e){ln(e.target.value)},placeholder:Ze(C,1)||"",ref:He},Bn,er,{autoComplete:Oe}))),i.createElement("div",{className:"".concat(u,"-active-bar"),style:(0,I.Z)((0,I.Z)({},rr),{},{width:nr,position:"absolute"})}),Yn,Jn)))}var Et=function(e){(0,z.Z)(n,e);var t=(0,H.Z)(n);function n(){var e;(0,F.Z)(this,n);for(var r=arguments.length,o=new Array(r),a=0;a 3&&void 0!==arguments[3]?arguments[3]:{},i=r.axis||"y";return{measure:function(){return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:e,n=arguments.length>2?arguments[2]:void 0;if(n.x.targetOffset=0,n.y.targetOffset=0,t!==e)for(var r=t;r&&r!=e;)n.x.targetOffset+=r.offsetLeft,n.y.targetOffset+=r.offsetTop,r=r.offsetParent;n.x.targetLength=t===e?t.scrollWidth:t.clientWidth,n.y.targetLength=t===e?t.scrollHeight:t.clientHeight,n.x.containerLength=e.clientWidth,n.y.containerLength=e.clientHeight}(e,r.target,n)},update:function(t){!function(e,t,n){Re(e,"x",t,n),Re(e,"y",t,n),t.time=n}(e,n,t),(r.offset||r.target)&&Qe(e,n,r)},notify:"function"===typeof t?function(){return t(n)}:Ge(t,n[i])}}function Ge(e,t){return e.pause(),e.forEachNative((function(e,t){var n,r,i=t.easing;if(e.updateDuration)i||(e.easing=s.V),e.updateDuration(1);else{var o={duration:1e3};i||(o.easing="linear"),null===(r=null===(n=e.effect)||void 0===n?void 0:n.updateTiming)||void 0===r||r.call(n,o)}})),function(){e.currentTime=t.progress}}var Ke=new WeakMap,Xe=new WeakMap,Ye=new WeakMap,Je=function(e){return e===document.documentElement?window:e};function $e(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.container,r=void 0===n?document.documentElement:n,i=(0,ne.__rest)(t,["container"]),o=Ye.get(r);o||(o=new Set,Ye.set(r,o));var a=Ze(),s=qe(r,e,a,i);if(o.add(s),!Ke.has(r)){var u=function(){var e,t=performance.now(),n=(0,Te.Z)(o);try{for(n.s();!(e=n.n()).done;){e.value.measure()}}catch(u){n.e(u)}finally{n.f()}var r,i=(0,Te.Z)(o);try{for(i.s();!(r=i.n()).done;){r.value.update(t)}}catch(u){i.e(u)}finally{i.f()}var a,s=(0,Te.Z)(o);try{for(s.s();!(a=s.n()).done;){a.value.notify()}}catch(u){s.e(u)}finally{s.f()}};Ke.set(r,u);var c=Je(r);window.addEventListener("resize",u,{passive:!0}),r!==document.documentElement&&Xe.set(r,Ne(r,u)),c.addEventListener("scroll",u,{passive:!0})}var l=Ke.get(r),d=requestAnimationFrame(l);return function(){var t;"function"!==typeof e&&e.stop(),cancelAnimationFrame(d);var n=Ye.get(r);if(n&&(n.delete(s),!n.size)){var i=Ke.get(r);Ke.delete(r),i&&(Je(r).removeEventListener("scroll",i),null===(t=Xe.get(r))||void 0===t||t(),window.removeEventListener("resize",i))}}}var et=n(74165);function tt(e,t){return function(e){return"object"===typeof e}(e)?e:e&&t?t[e]:void 0}var nt=void 0;function rt(){if(nt){var e=nt.sort(ot).map(at);e.forEach(st),e.forEach(st),nt=void 0}}function it(e){nt?(0,u.y)(nt,e):(nt=[e],requestAnimationFrame(rt))}var ot=function(e,t){return e.getDepth()-t.getDepth()},at=function(e){return e.animateUpdates()},st=function(e){return e.next()},ut=function(e,t){return new CustomEvent(e,{detail:{target:t}})};function ct(e,t,n){e.dispatchEvent(new CustomEvent(t,{detail:{originalEvent:n}}))}function lt(e,t,n){e.dispatchEvent(new CustomEvent(t,{detail:{originalEntry:n}}))}var dt=function(e,t,n){return function(r){r.pointerType&&"mouse"!==r.pointerType||(n(),ct(e,t,r))}},ft={inView:{isActive:function(e){return Boolean(e.inView)},subscribe:function(e,t,n){var r=t.enable,i=t.disable,o=n.inViewOptions,a=void 0===o?{}:o,s=a.once,u=(0,ne.__rest)(a,["once"]);return ke(e,(function(t){if(r(),lt(e,"viewenter",t),!s)return function(t){i(),lt(e,"viewleave",t)}}),u)}},hover:{isActive:function(e){return Boolean(e.hover)},subscribe:function(e,t){var n=t.enable,r=t.disable,i=dt(e,"hoverstart",n),o=dt(e,"hoverend",r);return e.addEventListener("pointerenter",i),e.addEventListener("pointerleave",o),function(){e.removeEventListener("pointerenter",i),e.removeEventListener("pointerleave",o)}}},press:{isActive:function(e){return Boolean(e.press)},subscribe:function(e,t){var n=t.enable,r=t.disable,i=function t(n){r(),ct(e,"pressend",n),window.removeEventListener("pointerup",t)},o=function(t){n(),ct(e,"pressstart",t),window.addEventListener("pointerup",i)};return e.addEventListener("pointerdown",o),function(){e.removeEventListener("pointerdown",o),window.removeEventListener("pointerup",i)}}}},ht=["initial","animate"].concat((0,te.Z)(Object.keys(ft)),["exit"]),pt=new WeakMap;function vt(){var e,t,n=(0,et.Z)().mark(m),r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},i=arguments.length>1?arguments[1]:void 0,o=i?i.getDepth()+1:0,a={initial:!0,animate:!0},c={},l={},d=(0,Te.Z)(ht);try{for(d.s();!(t=d.n()).done;){var f=t.value;l[f]="string"===typeof r[f]?r[f]:null===i||void 0===i?void 0:i.getContext()[f]}}catch(w){d.e(w)}finally{d.f()}var h=!1===r.initial?"animate":"initial",p=tt(r[h]||l[h],r.variants)||{},v=(0,ne.__rest)(p,["transition"]),g=Object.assign({},v);function m(){var t,i,o,u,c,l,d,f,h,p,m,y,b;return(0,et.Z)().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:o=v,v={},u={},c=(0,Te.Z)(ht),n.prev=4,c.s();case 6:if((l=c.n()).done){n.next=24;break}if(d=l.value,a[d]){n.next=10;break}return n.abrupt("continue",22);case 10:if(f=tt(r[d])){n.next=13;break}return n.abrupt("continue",22);case 13:n.t0=(0,et.Z)().keys(f);case 14:if((n.t1=n.t0()).done){n.next=22;break}if("transition"!==(h=n.t1.value)){n.next=18;break}return n.abrupt("continue",14);case 18:v[h]=f[h],u[h]=U(null!==(i=null!==(t=f.transition)&&void 0!==t?t:r.transition)&&void 0!==i?i:{},h),n.next=14;break;case 22:n.next=6;break;case 24:n.next=29;break;case 26:n.prev=26,n.t2=n.catch(4),c.e(n.t2);case 29:return n.prev=29,c.f(),n.finish(29);case 32:return p=new Set([].concat((0,te.Z)(Object.keys(v)),(0,te.Z)(Object.keys(o)))),m=[],p.forEach((function(t){var n,r,i;void 0===v[t]&&(v[t]=g[t]),r=o[t],i=v[t],typeof r===typeof i&&(Array.isArray(r)&&Array.isArray(i)?function(e,t){var n=t.length;if(n!==e.length)return!1;for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:"end";return function(n){var r=(n="end"===t?Math.min(n,.999):Math.max(n,.001))*e,i="end"===t?Math.floor(r):Math.ceil(r);return(0,c.u)(0,1,i/e)}},d=n(36896),f=n(53416),h={ease:u(.25,.1,.25,1),"ease-in":u(.42,0,1,1),"ease-in-out":u(.42,0,.58,1),"ease-out":u(0,0,.58,1)},p=/\((.*?)\)/;function v(e){if((0,d.m)(e))return e;if((0,f.U)(e))return u.apply(void 0,(0,r.Z)(e));if(h[e])return h[e];if(e.startsWith("steps")){var t=p.exec(e);if(t){var n=t[1].split(",");return l(parseFloat(n[0]),n[1].trim())}}return i.V}},99432:function(e,t,n){"use strict";n.r(t),n.d(t,{ScrollOffset:function(){return Fe},animate:function(){return ee},animateStyle:function(){return W},createMotionState:function(){return vt},createStyleString:function(){return bt},createStyles:function(){return gt},getAnimationData:function(){return a},getStyleName:function(){return V},glide:function(){return we},inView:function(){return ke},mountedStates:function(){return pt},resize:function(){return Ne},scroll:function(){return $e},spring:function(){return Ae},stagger:function(){return J},style:function(){return z},timeline:function(){return fe},withControls:function(){return G}});var r=n(4942),i=n(96653),o=new WeakMap;function a(e){return o.has(e)||o.set(e,{transforms:[],values:new Map}),o.get(e)}var s=n(52924),u=n(88069),c=["","X","Y","Z"],l={x:"translateX",y:"translateY",z:"translateZ"},d={syntax:"0){if(++n>=800)return arguments[0]}else n=0;return e.apply(void 0,arguments)}}},7465:function(e,t,n){var r=n(8407);e.exports=function(){this.__data__=new r,this.size=0}},3779:function(e){e.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},7599:function(e){e.exports=function(e){return this.__data__.get(e)}},4758:function(e){e.exports=function(e){return this.__data__.has(e)}},4309:function(e,t,n){var r=n(8407),i=n(7071),o=n(3369);e.exports=function(e,t){var n=this.__data__;if(n instanceof r){var a=n.__data__;if(!i||a.length<199)return a.push([e,t]),this.size=++n.size,this;n=this.__data__=new o(a)}return n.set(e,t),this.size=n.size,this}},2351:function(e){e.exports=function(e,t,n){for(var r=n-1,i=e.length;++r-1:!!l&&r(e,t,n)>-1}},5694:function(e,t,n){var r=n(9454),i=n(7005),o=Object.prototype,a=o.hasOwnProperty,s=o.propertyIsEnumerable,u=r(function(){return arguments}())?r:function(e){return i(e)&&a.call(e,"callee")&&!s.call(e,"callee")};e.exports=u},1469:function(e){var t=Array.isArray;e.exports=t},8612:function(e,t,n){var r=n(3560),i=n(1780);e.exports=function(e){return null!=e&&i(e.length)&&!r(e)}},9246:function(e,t,n){var r=n(8612),i=n(7005);e.exports=function(e){return i(e)&&r(e)}},4144:function(e,t,n){e=n.nmd(e);var r=n(5639),i=n(5062),o=t&&!t.nodeType&&t,a=o&&e&&!e.nodeType&&e,s=a&&a.exports===o?r.Buffer:void 0,u=(s?s.isBuffer:void 0)||i;e.exports=u},1609:function(e,t,n){var r=n(280),i=n(4160),o=n(5694),a=n(1469),s=n(8612),u=n(4144),c=n(5726),l=n(6719),d=Object.prototype.hasOwnProperty;e.exports=function(e){if(null==e)return!0;if(s(e)&&(a(e)||"string"==typeof e||"function"==typeof e.splice||u(e)||l(e)||o(e)))return!e.length;var t=i(e);if("[object Map]"==t||"[object Set]"==t)return!e.size;if(c(e))return!r(e).length;for(var n in e)if(d.call(e,n))return!1;return!0}},3560:function(e,t,n){var r=n(4239),i=n(3218);e.exports=function(e){if(!i(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},1780:function(e){e.exports=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}},6688:function(e,t,n){var r=n(5588),i=n(7518),o=n(1167),a=o&&o.isMap,s=a?i(a):r;e.exports=s},3218:function(e){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},7005:function(e){e.exports=function(e){return null!=e&&"object"==typeof e}},8630:function(e,t,n){var r=n(4239),i=n(5924),o=n(7005),a=Function.prototype,s=Object.prototype,u=a.toString,c=s.hasOwnProperty,l=u.call(Object);e.exports=function(e){if(!o(e)||"[object Object]"!=r(e))return!1;var t=i(e);if(null===t)return!0;var n=c.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&u.call(n)==l}},2928:function(e,t,n){var r=n(9221),i=n(7518),o=n(1167),a=o&&o.isSet,s=a?i(a):r;e.exports=s},7037:function(e,t,n){var r=n(4239),i=n(1469),o=n(7005);e.exports=function(e){return"string"==typeof e||!i(e)&&o(e)&&"[object String]"==r(e)}},3448:function(e,t,n){var r=n(4239),i=n(7005);e.exports=function(e){return"symbol"==typeof e||i(e)&&"[object Symbol]"==r(e)}},6719:function(e,t,n){var r=n(8749),i=n(7518),o=n(1167),a=o&&o.isTypedArray,s=a?i(a):r;e.exports=s},3674:function(e,t,n){var r=n(4636),i=n(280),o=n(8612);e.exports=function(e){return o(e)?r(e):i(e)}},1704:function(e,t,n){var r=n(4636),i=n(313),o=n(8612);e.exports=function(e){return o(e)?r(e,!0):i(e)}},928:function(e){e.exports=function(e){var t=null==e?0:e.length;return t?e[t-1]:void 0}},6486:function(e,t,n){var r;e=n.nmd(e),function(){var i,o="Expected a function",a="__lodash_hash_undefined__",s="__lodash_placeholder__",u=32,c=128,l=1/0,d=9007199254740991,f=NaN,h=4294967295,p=[["ary",c],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",u],["partialRight",64],["rearg",256]],v="[object Arguments]",g="[object Array]",m="[object Boolean]",y="[object Date]",b="[object Error]",A="[object Function]",w="[object GeneratorFunction]",C="[object Map]",k="[object Number]",x="[object Object]",_="[object Promise]",E="[object RegExp]",S="[object Set]",I="[object String]",O="[object Symbol]",D="[object WeakMap]",P="[object ArrayBuffer]",N="[object DataView]",T="[object Float32Array]",L="[object Float64Array]",M="[object Int8Array]",Z="[object Int16Array]",j="[object Int32Array]",R="[object Uint8Array]",B="[object Uint8ClampedArray]",F="[object Uint16Array]",V="[object Uint32Array]",z=/\b__p \+= '';/g,H=/\b(__p \+=) '' \+/g,W=/(__e\(.*?\)|\b__t\)) \+\n'';/g,U=/&(?:amp|lt|gt|quot|#39);/g,Q=/[&<>"']/g,q=RegExp(U.source),G=RegExp(Q.source),K=/<%-([\s\S]+?)%>/g,X=/<%([\s\S]+?)%>/g,Y=/<%=([\s\S]+?)%>/g,J=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,$=/^\w*$/,ee=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,te=/[\\^$.*+?()[\]{}|]/g,ne=RegExp(te.source),re=/^\s+/,ie=/\s/,oe=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ae=/\{\n\/\* \[wrapped with (.+)\] \*/,se=/,? & /,ue=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,ce=/[()=,{}\[\]\/\s]/,le=/\\(\\)?/g,de=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,fe=/\w*$/,he=/^[-+]0x[0-9a-f]+$/i,pe=/^0b[01]+$/i,ve=/^\[object .+?Constructor\]$/,ge=/^0o[0-7]+$/i,me=/^(?:0|[1-9]\d*)$/,ye=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,be=/($^)/,Ae=/['\n\r\u2028\u2029\\]/g,we="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",Ce="a-z\\xdf-\\xf6\\xf8-\\xff",ke="A-Z\\xc0-\\xd6\\xd8-\\xde",xe="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",_e="["+xe+"]",Ee="["+we+"]",Se="\\d+",Ie="["+Ce+"]",Oe="[^\\ud800-\\udfff"+xe+Se+"\\u2700-\\u27bf"+Ce+ke+"]",De="\\ud83c[\\udffb-\\udfff]",Pe="[^\\ud800-\\udfff]",Ne="(?:\\ud83c[\\udde6-\\uddff]){2}",Te="[\\ud800-\\udbff][\\udc00-\\udfff]",Le="["+ke+"]",Me="(?:"+Ie+"|"+Oe+")",Ze="(?:"+Le+"|"+Oe+")",je="(?:['\u2019](?:d|ll|m|re|s|t|ve))?",Re="(?:['\u2019](?:D|LL|M|RE|S|T|VE))?",Be="(?:"+Ee+"|"+De+")?",Fe="[\\ufe0e\\ufe0f]?",Ve=Fe+Be+"(?:\\u200d(?:"+[Pe,Ne,Te].join("|")+")"+Fe+Be+")*",ze="(?:"+["[\\u2700-\\u27bf]",Ne,Te].join("|")+")"+Ve,He="(?:"+[Pe+Ee+"?",Ee,Ne,Te,"[\\ud800-\\udfff]"].join("|")+")",We=RegExp("['\u2019]","g"),Ue=RegExp(Ee,"g"),Qe=RegExp(De+"(?="+De+")|"+He+Ve,"g"),qe=RegExp([Le+"?"+Ie+"+"+je+"(?="+[_e,Le,"$"].join("|")+")",Ze+"+"+Re+"(?="+[_e,Le+Me,"$"].join("|")+")",Le+"?"+Me+"+"+je,Le+"+"+Re,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Se,ze].join("|"),"g"),Ge=RegExp("[\\u200d\\ud800-\\udfff"+we+"\\ufe0e\\ufe0f]"),Ke=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Xe=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Ye=-1,Je={};Je[T]=Je[L]=Je[M]=Je[Z]=Je[j]=Je[R]=Je[B]=Je[F]=Je[V]=!0,Je[v]=Je[g]=Je[P]=Je[m]=Je[N]=Je[y]=Je[b]=Je[A]=Je[C]=Je[k]=Je[x]=Je[E]=Je[S]=Je[I]=Je[D]=!1;var $e={};$e[v]=$e[g]=$e[P]=$e[N]=$e[m]=$e[y]=$e[T]=$e[L]=$e[M]=$e[Z]=$e[j]=$e[C]=$e[k]=$e[x]=$e[E]=$e[S]=$e[I]=$e[O]=$e[R]=$e[B]=$e[F]=$e[V]=!0,$e[b]=$e[A]=$e[D]=!1;var et={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},tt=parseFloat,nt=parseInt,rt="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,it="object"==typeof self&&self&&self.Object===Object&&self,ot=rt||it||Function("return this")(),at=t&&!t.nodeType&&t,st=at&&e&&!e.nodeType&&e,ut=st&&st.exports===at,ct=ut&&rt.process,lt=function(){try{return st&&st.require&&st.require("util").types||ct&&ct.binding&&ct.binding("util")}catch(e){}}(),dt=lt&<.isArrayBuffer,ft=lt&<.isDate,ht=lt&<.isMap,pt=lt&<.isRegExp,vt=lt&<.isSet,gt=lt&<.isTypedArray;function mt(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function yt(e,t,n,r){for(var i=-1,o=null==e?0:e.length;++i-1},Un.prototype.set=function(e,t){var n=this.__data__,r=tr(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Qn.prototype.clear=function(){this.size=0,this.__data__={hash:new Wn,map:new(xn||Un),string:new Wn}},Qn.prototype.delete=function(e){var t=so(this,e).delete(e);return this.size-=t?1:0,t},Qn.prototype.get=function(e){return so(this,e).get(e)},Qn.prototype.has=function(e){return so(this,e).has(e)},Qn.prototype.set=function(e,t){var n=so(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},qn.prototype.add=qn.prototype.push=function(e){return this.__data__.set(e,a),this},qn.prototype.has=function(e){return this.__data__.has(e)},Gn.prototype.clear=function(){this.__data__=new Un,this.size=0},Gn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Gn.prototype.get=function(e){return this.__data__.get(e)},Gn.prototype.has=function(e){return this.__data__.has(e)},Gn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Un){var r=n.__data__;if(!xn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Qn(r)}return n.set(e,t),this.size=n.size,this};var dr=Pi(br),fr=Pi(Ar,!0);function hr(e,t){var n=!0;return dr(e,(function(e,r,i){return n=!!t(e,r,i)})),n}function pr(e,t,n){for(var r=-1,o=e.length;++rs&&(n=s-u),o=n;o>=0;o--){for(var d=!0,f=0;fi&&(r=i):r=i;var o,a=t.length;for(r>a/2&&(r=a/2),o=0;o>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}var i=this.length-t;if((void 0===n||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var o=!1;;)switch(r){case"hex":return _(this,e,t,n);case"utf8":case"utf-8":return E(this,e,t,n);case"ascii":case"latin1":case"binary":return S(this,e,t,n);case"base64":return I(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return O(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},h.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var N=4096;function T(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;i