blob: e4aa1fbf4a5a034513598eabc06c217532524639 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package entity
import (
"crypto/sha256"
"encoding/base64"
"io"
"sort"
"strings"
"time"
"cloud.google.com/go/datastore"
"beam.apache.org/playground/backend/internal/errors"
"beam.apache.org/playground/backend/internal/logger"
)
type IDMeta struct {
Salt string
IdLength int8
}
type FileEntity struct {
Name string `datastore:"name"`
Content string `datastore:"content,noindex"`
CntxLine int32 `datastore:"cntxLine"`
IsMain bool `datastore:"isMain"`
}
type SnippetEntity struct {
Key *datastore.Key `datastore:"__key__"`
OwnerId string `datastore:"ownerId"`
Sdk *datastore.Key `datastore:"sdk"`
PipeOpts string `datastore:"pipeOpts"`
Created time.Time `datastore:"created"`
LVisited time.Time `datastore:"lVisited"`
Origin string `datastore:"origin"`
VisitCount int `datastore:"visitCount"`
SchVer *datastore.Key `datastore:"schVer"`
NumberOfFiles int `datastore:"numberOfFiles"`
Complexity string `datastore:"complexity"`
PersistenceKey string `datastore:"persistenceKey,omitempty"`
Datasets []*DatasetNestedEntity `datastore:"datasets,omitempty"`
}
type DatasetEntity struct {
Key *datastore.Key `datastore:"__key__"`
Path string `datastore:"path"`
}
type DatasetNestedEntity struct {
Config string `datastore:"config"`
Dataset *datastore.Key `datastore:"dataset"`
Emulator string `datastore:"emulator"`
}
type Snippet struct {
*IDMeta
Snippet *SnippetEntity
Files []*FileEntity
}
// ID generates id according to content of the entity
func (s *Snippet) ID() (string, error) {
id, err := generateIDBasedOnContent(s.Salt, combineUniqueSnippetContent(s), s.IdLength)
if err != nil {
return "", err
}
return id, nil
}
func combineUniqueSnippetContent(snippet *Snippet) string {
var files []string
for _, file := range snippet.Files {
files = append(files, strings.TrimSpace(file.Content)+strings.TrimSpace(file.Name))
}
sort.Strings(files)
var contentBuilder strings.Builder
for _, file := range files {
contentBuilder.WriteString(file)
}
contentBuilder.WriteString(snippet.Snippet.Sdk.String())
contentBuilder.WriteString(strings.TrimSpace(snippet.Snippet.PipeOpts))
contentBuilder.WriteString(strings.TrimSpace(snippet.Snippet.Complexity))
contentBuilder.WriteString(snippet.Snippet.PersistenceKey)
return contentBuilder.String()
}
func generateIDBasedOnContent(salt, content string, length int8) (string, error) {
hash := sha256.New()
if _, err := io.WriteString(hash, salt); err != nil {
logger.Errorf("ID(): error during hash generation: %s", err.Error())
return "", errors.InternalError("Error during hash generation", "Error writing hash and salt")
}
hash.Write([]byte(content))
sum := hash.Sum(nil)
b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
base64.URLEncoding.Encode(b, sum)
hashLen := int(length)
for hashLen <= len(b) && b[hashLen-1] == '_' {
hashLen++
}
return string(b)[:hashLen], nil
}