mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-05-31 13:33:43 +00:00
Compare commits
6 Commits
33.0-dev4
...
33.0-dev10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fc3e38c1a | ||
|
|
09a0fca2c2 | ||
|
|
0437586908 | ||
|
|
f8181ccb07 | ||
|
|
414e5cfe5a | ||
|
|
2fac0009ea |
@@ -373,6 +373,7 @@ func handleIncomingMessageAsTapper(socketConnection *websocket.Conn) {
|
|||||||
func initializeDependencies() {
|
func initializeDependencies() {
|
||||||
dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() })
|
dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() })
|
||||||
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
|
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
|
||||||
|
dependency.RegisterGenerator(dependency.EntriesInserter, func() interface{} { return api.GetBasenineEntryInserterInstance() })
|
||||||
dependency.RegisterGenerator(dependency.EntriesProvider, func() interface{} { return &entries.BasenineEntriesProvider{} })
|
dependency.RegisterGenerator(dependency.EntriesProvider, func() interface{} { return &entries.BasenineEntriesProvider{} })
|
||||||
dependency.RegisterGenerator(dependency.EntriesSocketStreamer, func() interface{} { return &api.BasenineEntryStreamer{} })
|
dependency.RegisterGenerator(dependency.EntriesSocketStreamer, func() interface{} { return &api.BasenineEntryStreamer{} })
|
||||||
dependency.RegisterGenerator(dependency.EntryStreamerSocketConnector, func() interface{} { return &api.DefaultEntryStreamerSocketConnector{} })
|
dependency.RegisterGenerator(dependency.EntryStreamerSocketConnector, func() interface{} { return &api.DefaultEntryStreamerSocketConnector{} })
|
||||||
|
|||||||
@@ -25,10 +25,7 @@ import (
|
|||||||
"github.com/up9inc/mizu/agent/pkg/utils"
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/logger"
|
"github.com/up9inc/mizu/logger"
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var k8sResolver *resolver.Resolver
|
var k8sResolver *resolver.Resolver
|
||||||
@@ -103,20 +100,6 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
panic("Channel of captured messages is nil")
|
panic("Channel of captured messages is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
BasenineReconnect:
|
|
||||||
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log.Errorf("Can't establish a new connection to Basenine server: %v", err)
|
|
||||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
|
||||||
goto BasenineReconnect
|
|
||||||
}
|
|
||||||
if err = connection.InsertMode(); err != nil {
|
|
||||||
logger.Log.Errorf("Insert mode call failed: %v", err)
|
|
||||||
connection.Close()
|
|
||||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
|
||||||
goto BasenineReconnect
|
|
||||||
}
|
|
||||||
|
|
||||||
disableOASValidation := false
|
disableOASValidation := false
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
doc, contractContent, router, err := loadOAS(ctx)
|
doc, contractContent, router, err := loadOAS(ctx)
|
||||||
@@ -163,11 +146,9 @@ BasenineReconnect:
|
|||||||
|
|
||||||
providers.EntryAdded(len(data))
|
providers.EntryAdded(len(data))
|
||||||
|
|
||||||
if err = connection.SendText(string(data)); err != nil {
|
entryInserter := dependency.GetInstance(dependency.EntriesInserter).(EntryInserter)
|
||||||
logger.Log.Errorf("An error occured while inserting a new record to database: %v", err)
|
if err := entryInserter.Insert(mizuEntry); err != nil {
|
||||||
connection.Close()
|
logger.Log.Errorf("Error inserting entry, err: %v", err)
|
||||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
|
||||||
goto BasenineReconnect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
||||||
|
|||||||
71
agent/pkg/api/socket_data_inserter.go
Normal file
71
agent/pkg/api/socket_data_inserter.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
|
"github.com/up9inc/mizu/logger"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/tap/api"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EntryInserter interface {
|
||||||
|
Insert(entry *api.Entry) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasenineEntryInserter struct {
|
||||||
|
connection *basenine.Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance *BasenineEntryInserter
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
|
func GetBasenineEntryInserterInstance() *BasenineEntryInserter {
|
||||||
|
once.Do(func() {
|
||||||
|
instance = &BasenineEntryInserter{}
|
||||||
|
})
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BasenineEntryInserter) Insert(entry *api.Entry) error {
|
||||||
|
if e.connection == nil {
|
||||||
|
e.connection = initializeConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(entry)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshling entry, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.connection.SendText(string(data)); err != nil {
|
||||||
|
e.connection.Close()
|
||||||
|
e.connection = nil
|
||||||
|
|
||||||
|
return fmt.Errorf("error sending text to database, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeConnection() *basenine.Connection{
|
||||||
|
for {
|
||||||
|
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Can't establish a new connection to Basenine server: %v", err)
|
||||||
|
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = connection.InsertMode(); err != nil {
|
||||||
|
logger.Log.Errorf("Insert mode call failed: %v", err)
|
||||||
|
connection.Close()
|
||||||
|
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ type DependencyContainerType string
|
|||||||
const (
|
const (
|
||||||
ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency"
|
ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency"
|
||||||
OasGeneratorDependency = "OasGeneratorDependency"
|
OasGeneratorDependency = "OasGeneratorDependency"
|
||||||
|
EntriesInserter = "EntriesInserter"
|
||||||
EntriesProvider = "EntriesProvider"
|
EntriesProvider = "EntriesProvider"
|
||||||
EntriesSocketStreamer = "EntriesSocketStreamer"
|
EntriesSocketStreamer = "EntriesSocketStreamer"
|
||||||
EntryStreamerSocketConnector = "EntryStreamerSocketConnector"
|
EntryStreamerSocketConnector = "EntryStreamerSocketConnector"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (g *defaultOasGenerator) runGenerator() {
|
|||||||
g.dbMutex.Lock()
|
g.dbMutex.Lock()
|
||||||
defer g.dbMutex.Unlock()
|
defer g.dbMutex.Unlock()
|
||||||
logger.Log.Infof("Querying DB for OAS generator with query '%s'", g.entriesQuery)
|
logger.Log.Infof("Querying DB for OAS generator with query '%s'", g.entriesQuery)
|
||||||
if err := g.dbConn.Query("", g.entriesQuery, dataChan, metaChan); err != nil {
|
if err := g.dbConn.Query("latest", g.entriesQuery, dataChan, metaChan); err != nil {
|
||||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ BasenineReconnect:
|
|||||||
go handleMetaChannel(&wg, connection, meta)
|
go handleMetaChannel(&wg, connection, meta)
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
if err = connection.Query("", query, data, meta); err != nil {
|
if err = connection.Query("latest", query, data, meta); err != nil {
|
||||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||||
connection.Close()
|
connection.Close()
|
||||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ var grpcProtocol api.Protocol = api.Protocol{
|
|||||||
BackgroundColor: "#244c5a",
|
BackgroundColor: "#244c5a",
|
||||||
ForegroundColor: "#ffffff",
|
ForegroundColor: "#ffffff",
|
||||||
FontSize: 11,
|
FontSize: 11,
|
||||||
ReferenceLink: "https://grpc.github.io/grpc/core/md_doc_statuscodes.html",
|
ReferenceLink: "https://grpc.github.io/grpc/core/md_doc__p_r_o_t_o_c_o_l-_h_t_t_p2.html",
|
||||||
Ports: []string{"80", "443", "8080", "50051"},
|
Ports: []string{"80", "443", "8080", "50051"},
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
|||||||
|
|
||||||
test-pull-expect:
|
test-pull-expect:
|
||||||
@mkdir -p expect
|
@mkdir -p expect
|
||||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect8/kafka/\* expect
|
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect9/kafka/\* expect
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package kafka
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/text/cases"
|
|
||||||
"golang.org/x/text/language"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/fatih/camelcase"
|
"github.com/fatih/camelcase"
|
||||||
"github.com/ohler55/ojg/jp"
|
"github.com/ohler55/ojg/jp"
|
||||||
"github.com/ohler55/ojg/oj"
|
"github.com/ohler55/ojg/oj"
|
||||||
@@ -36,9 +37,14 @@ type KafkaWrapper struct {
|
|||||||
|
|
||||||
func representRequestHeader(data map[string]interface{}, rep []interface{}) []interface{} {
|
func representRequestHeader(data map[string]interface{}, rep []interface{}) []interface{} {
|
||||||
requestHeader, _ := json.Marshal([]api.TableData{
|
requestHeader, _ := json.Marshal([]api.TableData{
|
||||||
|
{
|
||||||
|
Name: "ApiKeyName",
|
||||||
|
Value: data["apiKeyName"].(string),
|
||||||
|
Selector: `request.apiKeyName`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "ApiKey",
|
Name: "ApiKey",
|
||||||
Value: apiNames[int(data["apiKey"].(float64))],
|
Value: int(data["apiKey"].(float64)),
|
||||||
Selector: `request.apiKey`,
|
Selector: `request.apiKey`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
|||||||
statusQuery := ""
|
statusQuery := ""
|
||||||
|
|
||||||
apiKey := ApiKey(entry.Request["apiKey"].(float64))
|
apiKey := ApiKey(entry.Request["apiKey"].(float64))
|
||||||
method := apiNames[apiKey]
|
method := entry.Request["apiKeyName"].(string)
|
||||||
methodQuery := fmt.Sprintf("request.apiKey == %d", int(entry.Request["apiKey"].(float64)))
|
methodQuery := fmt.Sprintf(`request.apiKeyName == "%s"`, method)
|
||||||
|
|
||||||
summary := ""
|
summary := ""
|
||||||
summaryQuery := ""
|
summaryQuery := ""
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Size int32 `json:"size"`
|
Size int32 `json:"size"`
|
||||||
|
ApiKeyName string `json:"apiKeyName"`
|
||||||
ApiKey ApiKey `json:"apiKey"`
|
ApiKey ApiKey `json:"apiKey"`
|
||||||
ApiVersion int16 `json:"apiVersion"`
|
ApiVersion int16 `json:"apiVersion"`
|
||||||
CorrelationID int32 `json:"correlationID"`
|
CorrelationID int32 `json:"correlationID"`
|
||||||
@@ -202,6 +203,7 @@ func ReadRequest(r io.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, ca
|
|||||||
|
|
||||||
request := &Request{
|
request := &Request{
|
||||||
Size: size,
|
Size: size,
|
||||||
|
ApiKeyName: apiNames[apiKey],
|
||||||
ApiKey: apiKey,
|
ApiKey: apiKey,
|
||||||
ApiVersion: apiVersion,
|
ApiVersion: apiVersion,
|
||||||
CorrelationID: correlationID,
|
CorrelationID: correlationID,
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ import (
|
|||||||
"github.com/up9inc/mizu/tap/diagnose"
|
"github.com/up9inc/mizu/tap/diagnose"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReassemblyStream interface {
|
|
||||||
Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, nextSeq reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool
|
|
||||||
ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext)
|
|
||||||
ReassemblyComplete(ac reassembly.AssemblerContext) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type tcpReassemblyStream struct {
|
type tcpReassemblyStream struct {
|
||||||
ident string
|
ident string
|
||||||
tcpState *reassembly.TCPSimpleFSM
|
tcpState *reassembly.TCPSimpleFSM
|
||||||
@@ -25,7 +19,7 @@ type tcpReassemblyStream struct {
|
|||||||
tcpStream api.TcpStream
|
tcpStream api.TcpStream
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTcpReassemblyStream(ident string, tcp *layers.TCP, fsmOptions reassembly.TCPSimpleFSMOptions, stream api.TcpStream) ReassemblyStream {
|
func NewTcpReassemblyStream(ident string, tcp *layers.TCP, fsmOptions reassembly.TCPSimpleFSMOptions, stream api.TcpStream) reassembly.Stream {
|
||||||
return &tcpReassemblyStream{
|
return &tcpReassemblyStream{
|
||||||
ident: ident,
|
ident: ident,
|
||||||
tcpState: reassembly.NewTCPSimpleFSM(fsmOptions),
|
tcpState: reassembly.NewTCPSimpleFSM(fsmOptions),
|
||||||
|
|||||||
@@ -96,8 +96,3 @@
|
|||||||
|
|
||||||
& .servicesFilterList
|
& .servicesFilterList
|
||||||
height: calc(100% - 30px - 52px)
|
height: calc(100% - 30px - 52px)
|
||||||
|
|
||||||
.totalSelected
|
|
||||||
font-size: 12px
|
|
||||||
color: $light-blue-color
|
|
||||||
font-weight: 700
|
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
|||||||
const [checkedProtocols, setCheckedProtocols] = useState([])
|
const [checkedProtocols, setCheckedProtocols] = useState([])
|
||||||
const [checkedServices, setCheckedServices] = useState([])
|
const [checkedServices, setCheckedServices] = useState([])
|
||||||
const [serviceMapApiData, setServiceMapApiData] = useState<ServiceMapGraph>({ edges: [], nodes: [] })
|
const [serviceMapApiData, setServiceMapApiData] = useState<ServiceMapGraph>({ edges: [], nodes: [] })
|
||||||
const [servicesSearchVal, setServicesSearchVal] = useState("")
|
|
||||||
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
|
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
|
||||||
const [isFilterClicked, setIsFilterClicked] = useState(true)
|
const [isFilterClicked, setIsFilterClicked] = useState(true)
|
||||||
|
|
||||||
@@ -219,22 +218,14 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
|||||||
<Resizeable minWidth={170} maxWidth={320}>
|
<Resizeable minWidth={170} maxWidth={320}>
|
||||||
<div className={styles.filterWrapper}>
|
<div className={styles.filterWrapper}>
|
||||||
<div className={styles.protocolsFilterList}>
|
<div className={styles.protocolsFilterList}>
|
||||||
<h3 className={styles.subSectionHeader} style={{ marginLeft: "10px" }}>
|
<SelectList items={getProtocolsForFilter} checkBoxWidth="5%" tableName={"PROTOCOLS"} multiSelect={true}
|
||||||
PROTOCOLS
|
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters}
|
||||||
<span className={styles.totalSelected}> ({checkedProtocols.length})</span>
|
inputSearchClass={styles.servicesFilterSearch} isFilterable={false}/>
|
||||||
</h3>
|
|
||||||
<SelectList items={getProtocolsForFilter} checkBoxWidth="5%" tableName={"All"} multiSelect={true}
|
|
||||||
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters} />
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.servicesFilter}>
|
<div className={styles.servicesFilter}>
|
||||||
<h3 className={styles.subSectionHeader} style={{ marginLeft: "10px" }}>
|
<div className={styles.servicesFilterList}>
|
||||||
SERVICES
|
<SelectList items={getServicesForFilter} tableName={"SERVICES"} tableClassName={styles.filters} multiSelect={true}
|
||||||
<span className={styles.totalSelected}> ({checkedServices.length})</span>
|
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} inputSearchClass={styles.servicesFilterSearch}/>
|
||||||
</h3>
|
|
||||||
<input className={commonClasses.textField + ` ${styles.servicesFilterSearch}`} placeholder="Search" value={servicesSearchVal} onChange={(event) => setServicesSearchVal(event.target.value)} />
|
|
||||||
<div className={styles.servicesFilterList}>
|
|
||||||
<SelectList items={getServicesForFilter} tableName={"All"} tableClassName={styles.filters} multiSelect={true} searchValue={servicesSearchVal}
|
|
||||||
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Radio from "./Radio";
|
|||||||
import styles from './style/SelectList.module.sass'
|
import styles from './style/SelectList.module.sass'
|
||||||
import NoDataMessage from "./NoDataMessage";
|
import NoDataMessage from "./NoDataMessage";
|
||||||
import Checkbox from "./Checkbox";
|
import Checkbox from "./Checkbox";
|
||||||
|
import { useCommonStyles } from "../../helpers/commonStyle";
|
||||||
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -10,14 +11,17 @@ export interface Props {
|
|||||||
tableName: string;
|
tableName: string;
|
||||||
checkedValues?: string[];
|
checkedValues?: string[];
|
||||||
multiSelect: boolean;
|
multiSelect: boolean;
|
||||||
searchValue?: string;
|
|
||||||
setCheckedValues: (newValues) => void;
|
setCheckedValues: (newValues) => void;
|
||||||
tableClassName?
|
tableClassName?;
|
||||||
checkBoxWidth?: string
|
checkBoxWidth?: string;
|
||||||
|
inputSearchClass? : string
|
||||||
|
isFilterable? : boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], multiSelect = true, searchValue = "", setCheckedValues, tableClassName,
|
const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], multiSelect = true, setCheckedValues, tableClassName,
|
||||||
checkBoxWidth = 50 }) => {
|
checkBoxWidth = 50 ,inputSearchClass,isFilterable = true}) => {
|
||||||
|
const commonClasses = useCommonStyles();
|
||||||
|
const [searchValue, setSearchValue] = useState("")
|
||||||
const noItemsMessage = "No items to show";
|
const noItemsMessage = "No items to show";
|
||||||
const [headerChecked, setHeaderChecked] = useState(false)
|
const [headerChecked, setHeaderChecked] = useState(false)
|
||||||
|
|
||||||
@@ -73,11 +77,10 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
|||||||
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={headerChecked}
|
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={headerChecked}
|
||||||
onToggle={(isChecked) => toggleAll(isChecked)} /></th>
|
onToggle={(isChecked) => toggleAll(isChecked)} /></th>
|
||||||
<th>
|
<th>
|
||||||
{tableName}
|
All
|
||||||
</th>
|
</th>
|
||||||
</tr> :
|
</tr> :
|
||||||
<tr style={{ borderBottomWidth: "2px" }}>
|
<tr>
|
||||||
<th>{tableName}</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
const tableBody = filteredValues.length === 0 ?
|
const tableBody = filteredValues.length === 0 ?
|
||||||
@@ -100,7 +103,14 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return <div className={tableClassName ? tableClassName + ` ${styles.selectListTable}` : ` ${styles.selectListTable}`}>
|
return <React.Fragment>
|
||||||
|
<h3 className={styles.subSectionHeader}>
|
||||||
|
{tableName}
|
||||||
|
<span className={styles.totalSelected}> ({checkedValues.length})</span>
|
||||||
|
</h3>
|
||||||
|
{isFilterable && <input className={commonClasses.textField + ` ${inputSearchClass}`} placeholder="Search" value={searchValue}
|
||||||
|
onChange={(event) => setSearchValue(event.target.value)} data-cy="searchInput" />}
|
||||||
|
<div className={tableClassName ? tableClassName + ` ${styles.selectListTable}` : ` ${styles.selectListTable}`} style={{marginTop: !multiSelect ? "20px": ""}}>
|
||||||
<table cellPadding={5} style={{ borderCollapse: "collapse" }}>
|
<table cellPadding={5} style={{ borderCollapse: "collapse" }}>
|
||||||
<thead>
|
<thead>
|
||||||
{tableHead}
|
{tableHead}
|
||||||
@@ -110,6 +120,7 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectList;
|
export default SelectList;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
@import '../../../variables.module'
|
@import '../../../variables.module'
|
||||||
|
@import '../../../components'
|
||||||
|
|
||||||
|
|
||||||
.selectListTable
|
.selectListTable
|
||||||
overflow: auto
|
overflow: auto
|
||||||
@@ -17,6 +19,7 @@
|
|||||||
position: sticky
|
position: sticky
|
||||||
top: 0
|
top: 0
|
||||||
background: $main-background-color
|
background: $main-background-color
|
||||||
|
font-size: 12px
|
||||||
|
|
||||||
tr
|
tr
|
||||||
border-bottom-width: 1px
|
border-bottom-width: 1px
|
||||||
@@ -27,7 +30,15 @@
|
|||||||
td
|
td
|
||||||
color: $light-gray
|
color: $light-gray
|
||||||
padding: 10px
|
padding: 10px
|
||||||
font-size: 16px
|
font-size: 11px
|
||||||
|
font-weight: 600
|
||||||
|
padding-top: 5px
|
||||||
|
padding-bottom: 5px
|
||||||
|
|
||||||
.nowrap
|
.nowrap
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
|
|
||||||
|
.totalSelected
|
||||||
|
font-size: 12px
|
||||||
|
color: $light-blue-color
|
||||||
|
font-weight: 700
|
||||||
|
|||||||
Reference in New Issue
Block a user