go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/resources/docs/src/lrdocs/lrdocs.tsx (about)

     1  /**
     2   * Copyright (c) Mondoo, Inc.
     3   * SPDX-License-Identifier: BUSL-1.1
     4   */
     5  
     6  import * as React from 'react';
     7  import styled, { ThemeProvider } from 'styled-components';
     8  
     9  import snapshots from '../../static/snapshots.json';
    10  import { JsxElement } from 'typescript';
    11  
    12  
    13  type LrArg = {
    14    ID: string
    15    Type: LrType
    16  }
    17  
    18  type LrSimpleArg = {
    19    Type: string
    20  }
    21  
    22  type LrType = {
    23    ListType?: {
    24      Type: LrType
    25    }
    26    MapType?: {
    27      Key: LrSimpleArg
    28      Value: LrType
    29    }
    30    SimpleType?: {
    31      Type: string
    32    }    
    33  }
    34  
    35  type LrField = {
    36    Args?: {
    37      List?: LrSimpleArg[]
    38    }
    39    ID: string
    40    Type: LrType
    41    updated?: boolean
    42  }
    43  
    44  type LrListResource = {
    45    Type: {
    46      Type: string
    47    }
    48  }
    49  
    50  type LrInit = {
    51    Args: LrArg[]
    52  }
    53  
    54  type LrResource = {
    55    Body: {
    56      Fields: LrField[] | null
    57      Inits: LrInit[] | null
    58    }
    59    ID: string
    60    ListType?: LrListResource
    61    updated?: boolean
    62  }
    63  
    64  type LrSnapshot = {
    65    version: string
    66    Resources: LrResource[]
    67  }
    68  
    69  type LrDocsState = {
    70    metadata?: LrSnapshot[]
    71    selected?: LrSnapshot
    72  }
    73  export class LrDocs extends React.Component<{}, LrDocsState> {
    74    state: LrDocsState = {}
    75  
    76    render() {
    77      if (this.state.metadata == null) {
    78        this.load_metadata()
    79        return "loading..."
    80      }
    81  
    82      let versions = this.state.metadata.map(x => x.version);
    83      let { selected } = this.state;
    84  
    85      let idx = versions.indexOf(selected.version);
    86      let prev = selected;
    87      if (idx <= versions.length-2) {
    88        prev = this.state.metadata[idx+1]
    89      }
    90  
    91      return (
    92        <SiteStructure>
    93          <Versions
    94            versions={versions}
    95            selected={selected.version}
    96            onSelect={(version) => {
    97              let selected = this.state.metadata.find(v => v.version == version)
    98              this.setState({
    99                selected,
   100              })
   101            }}
   102          />
   103          <Resources snapshot={selected} prev={prev} />
   104        </SiteStructure>
   105      )
   106    }
   107  
   108    load_metadata() {
   109      let data = snapshots;
   110      let versions = Object.keys(data).sort()
   111      
   112      let metadata = versions.map(v => {
   113        let res = data[v] as LrSnapshot;
   114        res.version = v;
   115        return res
   116      }).sort(
   117        (a,b) => a.version > b.version ? -1 : 1
   118      )
   119      
   120      let selected = metadata[0];
   121  
   122      setTimeout(() => {
   123        this.setState({
   124          metadata,
   125          selected
   126        })
   127      })    
   128    }
   129  }
   130  
   131  const SiteStructure = styled.div`
   132    display: flex;
   133  `
   134  
   135  const Nav = styled.div`
   136    padding: 18px;
   137    background: ${props => props.theme.colors.bgDarker};
   138    box-shadow: ${props => props.theme.shadows.default};
   139    margin-right: 12px;
   140    min-height: 100vh;
   141  `
   142  
   143  const NavItem = styled.div<{
   144    selected: boolean
   145  }>`
   146    color: ${props => props.selected ? props.theme.colors.primary : "inherit"};
   147    cursor: pointer;
   148  `
   149  
   150  type VersionsProps = {
   151    versions: string[]
   152    selected: string
   153    onSelect: (string) => void
   154  }
   155  export class Versions extends React.Component<VersionsProps, {}> {
   156    render() {
   157      return (
   158        <Nav>
   159          {this.props.versions.map(v => (
   160            <NavItem key={v}
   161              selected={v == this.props.selected}
   162              onClick={() => this.props.onSelect(v)}
   163            >{v}</NavItem>
   164          ))}
   165        </Nav>
   166      )
   167    }
   168  }
   169  
   170  const Container = styled.div`
   171    padding: 24px;
   172  `
   173  
   174  const InfoLine = styled.div`
   175    padding: 0 0 12px 0;
   176  
   177    & span {
   178      margin-right: 48px;
   179      display: inline-block;
   180    }
   181  `
   182  
   183  type ResourcesProps = {
   184    snapshot: LrSnapshot
   185    prev: LrSnapshot
   186  }
   187  export class Resources extends React.Component<ResourcesProps, {}> {
   188    render() {
   189      let snap = this.props.snapshot
   190      window.SNAP = snap; // yeah yeah TODO: remove me
   191  
   192      let resources = snap.Resources.sort((a,b) => (a.ID > b.ID) ? 1 : -1 )
   193      // resources = this.computeUpdatedResources(resources, this.props.prev.Resources)
   194  
   195      let fieldsCnt = 0
   196      resources.forEach(r => {
   197        if (r.Body.Fields != null) {
   198          fieldsCnt += r.Body.Fields.length
   199        }
   200      })
   201  
   202      let cards = resources.map((resource, idx) => {
   203        return <Resource resource={resource} key={idx} />
   204      })
   205      
   206      return (
   207        <Container>
   208          <InfoLine>
   209            <span>Version: {snap.version}</span>
   210            <span>Resources: {snap.Resources.length}</span>
   211            <span>Fields: {fieldsCnt}</span>
   212          </InfoLine>
   213          <Cards>
   214            {cards}
   215          </Cards>
   216        </Container>
   217      )
   218    }
   219  
   220    computeUpdatedResources(resources: LrResource[], prevResources: LrResource[]): LrResource[] {
   221      let prev = {}
   222      prevResources.map(x => prev[x.ID] = x)
   223  
   224      let updatedResources = []
   225      resources.forEach(resource => {
   226        let prevResource = prev[resource.ID]
   227        if (prevResource == null) {
   228          resource.updated = true
   229          resource.Body.Fields.forEach(field => field.updated = true)
   230          updatedResources.push(resource)
   231          return
   232        }
   233  
   234        if (resource.Body.Fields == null) {
   235          return
   236        }
   237  
   238        let hasUpdates = false
   239        resource.Body.Fields.forEach((field) => {
   240          let updated = prevResource.Body.Fields.find(f => f.ID == field.ID) == null;
   241          if (updated) {
   242            field.updated = true
   243            hasUpdates = true
   244          }
   245        })
   246  
   247        if (hasUpdates) {
   248          updatedResources.push(resource)
   249          resource.updated = false
   250        }
   251      })
   252  
   253      return updatedResources
   254    }
   255  }
   256  
   257  const Cards = styled.div`
   258    display: flex;
   259    flex-wrap: wrap;
   260  `;
   261  
   262  const Card = styled.div`
   263    max-width: 400px;
   264    min-width: 200px;
   265    min-height: 100px;
   266    font-size: 24px;
   267    border: 2px solid #333;
   268    border-radius: 7px;
   269    margin: 0 24px 24px 0;
   270    padding: 15px 24px 24px 24px;
   271    background: ${props => props.theme.colors.bgDarker};
   272  
   273    &:hover {
   274      box-shadow: ${props => props.theme.shadows.default};
   275    }
   276  `
   277  const Name = styled.span<{
   278    updated?: boolean
   279  }>`
   280    color: ${props => (props.updated !== false) ? props.theme.colors.primary : "white"};
   281  `
   282  
   283  const Inits = styled.span`
   284    font-size: 16px;
   285  `
   286  
   287  const FieldName = styled.span<{
   288    updated?: boolean
   289  }>`
   290    color: ${props => (props.updated === true) ? props.theme.colors.primary : "inherit"};
   291    cursor: pointer;
   292  
   293    &:hover {
   294      color: ${props => props.theme.colors.secondary};
   295    }
   296  `
   297  
   298  const FieldType = styled.span`
   299    font-size: 16px;
   300    color: #aaa;
   301  `;
   302  
   303  type ResourceProps = {
   304    resource: LrResource
   305  }
   306  export class Resource extends React.Component<ResourceProps, {}> {
   307    render() {
   308      let { resource } = this.props
   309  
   310      let inits = this.renderInit(resource.Body.Inits)
   311      let listType = null;
   312      if (resource.ListType != null) {
   313        listType = "[]"+resource.ListType.Type.Type
   314      }
   315  
   316      let fields = (resource.Body.Fields || []).map((field) => this.renderField(field))
   317  
   318      return (
   319        <Card>
   320          <div>
   321            <Name updated={resource.updated}>{resource.ID}</Name> <Inits>{inits}</Inits>
   322          </div>
   323          {listType}
   324          {fields}
   325        </Card>
   326      )
   327    }
   328  
   329    renderInit(inits: LrInit[] | null): React.ReactElement | null {
   330      if (inits == null || inits.length == 0) {
   331        return (
   332          null
   333        )
   334      }
   335  
   336      if (inits.length != 1) {
   337        console.log("too many inits, ignoring everything else:")
   338        console.log(inits)
   339      }
   340  
   341      let init = inits[0];
   342      return (
   343        <span>
   344          ( {init.Args.map((arg) => arg.ID + " " + renderLrType(arg.Type)).join(", ")} )
   345        </span>
   346      )
   347    }
   348  
   349    renderField(field: LrField): React.ReactElement {
   350      return (
   351        <div key={field.ID}>
   352          <FieldName updated={field.updated}>{field.ID}</FieldName> <FieldType>{renderLrType(field.Type)}</FieldType>
   353        </div>
   354      )
   355    }
   356  }
   357  
   358  function renderLrType(t: LrType): string {
   359    if (t.SimpleType != null) return t.SimpleType.Type;
   360    if (t.ListType != null) return "[]"+renderLrType(t.ListType.Type);
   361    if (t.MapType != null) return "map["+t.MapType.Key.Type+"]"+renderLrType(t.MapType.Value);
   362    return "?";
   363  }