@contember/cli 1.1.0-alpha.6 → 1.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/src/commands/deploy/DeployCommand.d.ts +3 -1
  2. package/dist/src/commands/deploy/DeployCommand.d.ts.map +1 -1
  3. package/dist/src/commands/deploy/DeployCommand.js +83 -10
  4. package/dist/src/commands/deploy/DeployCommand.js.map +1 -1
  5. package/dist/src/commands/migrations/MigrationAmendCommand.js +2 -2
  6. package/dist/src/commands/migrations/MigrationAmendCommand.js.map +1 -1
  7. package/dist/src/commands/migrations/MigrationDiffCommand.js +3 -3
  8. package/dist/src/commands/migrations/MigrationDiffCommand.js.map +1 -1
  9. package/dist/src/commands/migrations/MigrationExecuteCommand.js +1 -1
  10. package/dist/src/commands/migrations/MigrationExecuteCommand.js.map +1 -1
  11. package/dist/src/commands/migrations/MigrationExecuteHelper.d.ts.map +1 -1
  12. package/dist/src/commands/migrations/MigrationExecuteHelper.js +3 -0
  13. package/dist/src/commands/migrations/MigrationExecuteHelper.js.map +1 -1
  14. package/dist/src/commands/migrations/MigrationStatusCommand.js +1 -1
  15. package/dist/src/commands/migrations/MigrationStatusCommand.js.map +1 -1
  16. package/dist/src/commands/migrations/SystemApiClientResolver.js +1 -1
  17. package/dist/src/commands/migrations/SystemApiClientResolver.js.map +1 -1
  18. package/dist/src/commands/project/ProjectGenerateDocumentation.d.ts +13 -0
  19. package/dist/src/commands/project/ProjectGenerateDocumentation.d.ts.map +1 -0
  20. package/dist/src/commands/project/ProjectGenerateDocumentation.js +39 -0
  21. package/dist/src/commands/project/ProjectGenerateDocumentation.js.map +1 -0
  22. package/dist/src/commands/project/ProjectPrintSchemaCommand.d.ts +1 -1
  23. package/dist/src/commands/project/ProjectPrintSchemaCommand.d.ts.map +1 -1
  24. package/dist/src/commands/project/ProjectPrintSchemaCommand.js +15 -7
  25. package/dist/src/commands/project/ProjectPrintSchemaCommand.js.map +1 -1
  26. package/dist/src/commands/project/ProjectValidateCommand.js +2 -2
  27. package/dist/src/commands/project/ProjectValidateCommand.js.map +1 -1
  28. package/dist/src/commands/project/index.d.ts +1 -0
  29. package/dist/src/commands/project/index.d.ts.map +1 -1
  30. package/dist/src/commands/project/index.js +1 -0
  31. package/dist/src/commands/project/index.js.map +1 -1
  32. package/dist/src/commands/tenant/CreateApiKeyCommand.js +1 -1
  33. package/dist/src/commands/tenant/CreateApiKeyCommand.js.map +1 -1
  34. package/dist/src/commands/tenant/InviteCommand.js +1 -1
  35. package/dist/src/commands/tenant/InviteCommand.js.map +1 -1
  36. package/dist/src/commands/tenant/ResetPasswordCommand.js +1 -1
  37. package/dist/src/commands/tenant/ResetPasswordCommand.js.map +1 -1
  38. package/dist/src/commands/tenant/SignInCommand.js +1 -1
  39. package/dist/src/commands/tenant/SignInCommand.js.map +1 -1
  40. package/dist/src/run.js +1 -0
  41. package/dist/src/run.js.map +1 -1
  42. package/dist/src/tsconfig.tsbuildinfo +1 -1
  43. package/dist/src/utils/instance/environment.d.ts +0 -1
  44. package/dist/src/utils/instance/environment.d.ts.map +1 -1
  45. package/dist/src/utils/instance/environment.js +0 -5
  46. package/dist/src/utils/instance/environment.js.map +1 -1
  47. package/dist/src/utils/instance/interactiveUtils.d.ts.map +1 -1
  48. package/dist/src/utils/instance/interactiveUtils.js +1 -2
  49. package/dist/src/utils/instance/interactiveUtils.js.map +1 -1
  50. package/dist/src/utils/project/components/EntityInfo.d.ts +11 -0
  51. package/dist/src/utils/project/components/EntityInfo.d.ts.map +1 -0
  52. package/dist/src/utils/project/components/EntityInfo.js +54 -0
  53. package/dist/src/utils/project/components/EntityInfo.js.map +1 -0
  54. package/dist/src/utils/project/components/EntityLink.d.ts +5 -0
  55. package/dist/src/utils/project/components/EntityLink.d.ts.map +1 -0
  56. package/dist/src/utils/project/components/EntityLink.js +8 -0
  57. package/dist/src/utils/project/components/EntityLink.js.map +1 -0
  58. package/dist/src/utils/project/components/FieldLink.d.ts +7 -0
  59. package/dist/src/utils/project/components/FieldLink.d.ts.map +1 -0
  60. package/dist/src/utils/project/components/FieldLink.js +13 -0
  61. package/dist/src/utils/project/components/FieldLink.js.map +1 -0
  62. package/dist/src/utils/project/components/FieldPermissions.d.ts +7 -0
  63. package/dist/src/utils/project/components/FieldPermissions.d.ts.map +1 -0
  64. package/dist/src/utils/project/components/FieldPermissions.js +17 -0
  65. package/dist/src/utils/project/components/FieldPermissions.js.map +1 -0
  66. package/dist/src/utils/project/components/FieldType.d.ts +6 -0
  67. package/dist/src/utils/project/components/FieldType.d.ts.map +1 -0
  68. package/dist/src/utils/project/components/FieldType.js +59 -0
  69. package/dist/src/utils/project/components/FieldType.js.map +1 -0
  70. package/dist/src/utils/project/components/OnDelete.d.ts +9 -0
  71. package/dist/src/utils/project/components/OnDelete.d.ts.map +1 -0
  72. package/dist/src/utils/project/components/OnDelete.js +70 -0
  73. package/dist/src/utils/project/components/OnDelete.js.map +1 -0
  74. package/dist/src/utils/project/components/ProjectInfo.d.ts +7 -0
  75. package/dist/src/utils/project/components/ProjectInfo.d.ts.map +1 -0
  76. package/dist/src/utils/project/components/ProjectInfo.js +28 -0
  77. package/dist/src/utils/project/components/ProjectInfo.js.map +1 -0
  78. package/dist/src/utils/project/components/SinglePermission.d.ts +7 -0
  79. package/dist/src/utils/project/components/SinglePermission.d.ts.map +1 -0
  80. package/dist/src/utils/project/components/SinglePermission.js +15 -0
  81. package/dist/src/utils/project/components/SinglePermission.js.map +1 -0
  82. package/dist/src/utils/project/components/utils.d.ts +3 -0
  83. package/dist/src/utils/project/components/utils.d.ts.map +1 -0
  84. package/dist/src/utils/project/components/utils.js +8 -0
  85. package/dist/src/utils/project/components/utils.js.map +1 -0
  86. package/dist/src/utils/{project.d.ts → project/loadSchema.d.ts} +1 -1
  87. package/dist/src/utils/project/loadSchema.d.ts.map +1 -0
  88. package/dist/src/utils/{project.js → project/loadSchema.js} +1 -1
  89. package/dist/src/utils/project/loadSchema.js.map +1 -0
  90. package/dist/src/utils/project/projectDescribe.d.ts +3 -0
  91. package/dist/src/utils/project/projectDescribe.d.ts.map +1 -0
  92. package/dist/src/utils/project/projectDescribe.js +29 -0
  93. package/dist/src/utils/project/projectDescribe.js.map +1 -0
  94. package/dist/src/utils/project/resources/out.css +759 -0
  95. package/dist/src/utils/schema/collectReferences.d.ts +10 -0
  96. package/dist/src/utils/schema/collectReferences.d.ts.map +1 -0
  97. package/dist/src/utils/schema/collectReferences.js +35 -0
  98. package/dist/src/utils/schema/collectReferences.js.map +1 -0
  99. package/dist/src/utils/tenant.d.ts +5 -2
  100. package/dist/src/utils/tenant.d.ts.map +1 -1
  101. package/dist/src/utils/tenant.js +10 -7
  102. package/dist/src/utils/tenant.js.map +1 -1
  103. package/package.json +17 -11
  104. package/postcss.config.js +6 -0
  105. package/src/commands/deploy/DeployCommand.ts +88 -14
  106. package/src/commands/migrations/MigrationAmendCommand.ts +1 -1
  107. package/src/commands/migrations/MigrationDiffCommand.ts +2 -2
  108. package/src/commands/migrations/MigrationExecuteCommand.ts +1 -1
  109. package/src/commands/migrations/MigrationExecuteHelper.ts +3 -0
  110. package/src/commands/migrations/MigrationStatusCommand.ts +1 -1
  111. package/src/commands/migrations/SystemApiClientResolver.ts +1 -1
  112. package/src/commands/project/ProjectGenerateDocumentation.ts +44 -0
  113. package/src/commands/project/ProjectPrintSchemaCommand.ts +13 -7
  114. package/src/commands/project/ProjectValidateCommand.ts +1 -1
  115. package/src/commands/project/index.ts +1 -0
  116. package/src/commands/tenant/CreateApiKeyCommand.ts +1 -1
  117. package/src/commands/tenant/InviteCommand.ts +1 -1
  118. package/src/commands/tenant/ResetPasswordCommand.ts +1 -1
  119. package/src/commands/tenant/SignInCommand.ts +1 -1
  120. package/src/run.ts +2 -0
  121. package/src/tsconfig.json +5 -1
  122. package/src/utils/instance/environment.ts +0 -3
  123. package/src/utils/instance/interactiveUtils.ts +2 -2
  124. package/src/utils/project/components/EntityInfo.tsx +82 -0
  125. package/src/utils/project/components/EntityLink.tsx +6 -0
  126. package/src/utils/project/components/FieldLink.tsx +10 -0
  127. package/src/utils/project/components/FieldPermissions.tsx +14 -0
  128. package/src/utils/project/components/FieldType.tsx +54 -0
  129. package/src/utils/project/components/OnDelete.tsx +72 -0
  130. package/src/utils/project/components/ProjectInfo.tsx +48 -0
  131. package/src/utils/project/components/SinglePermission.tsx +13 -0
  132. package/src/utils/project/components/utils.ts +3 -0
  133. package/src/utils/{project.ts → project/loadSchema.ts} +0 -0
  134. package/src/utils/project/projectDescribe.tsx +26 -0
  135. package/src/utils/project/resources/style.css +3 -0
  136. package/src/utils/schema/collectReferences.ts +40 -0
  137. package/src/utils/tenant.ts +14 -6
  138. package/tailwind.config.js +7 -0
  139. package/dist/src/utils/project.d.ts.map +0 -1
  140. package/dist/src/utils/project.js.map +0 -1
package/src/run.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  MigrationRebaseCommand,
12
12
  MigrationStatusCommand,
13
13
  ProjectCreateCommand,
14
+ ProjectGenerateDocumentation,
14
15
  ProjectPrintSchemaCommand,
15
16
  ProjectValidateCommand,
16
17
  ResetPasswordCommand,
@@ -35,6 +36,7 @@ import {
35
36
  ['project:create']: () => new ProjectCreateCommand(),
36
37
  ['project:validate']: () => new ProjectValidateCommand(),
37
38
  ['project:print-schema']: () => new ProjectPrintSchemaCommand(),
39
+ ['project:generate-doc']: () => new ProjectGenerateDocumentation(),
38
40
  ['tenant:sign-in']: () => new SignInCommand(),
39
41
  ['tenant:create-api-key']: () => new CreateApiKeyCommand(),
40
42
  ['tenant:invite']: () => new InviteCommand(),
package/src/tsconfig.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "extends": "../tsconfig.settings.json",
3
3
  "compilerOptions": {
4
- "outDir": "../dist/src"
4
+ "outDir": "../dist/src",
5
+ "jsx": "react",
6
+ "jsxFactory": "h",
7
+ "lib": ["dom"],
8
+ "jsxFragmentFactory": "Fragment"
5
9
  },
6
10
  "references": [
7
11
  {
@@ -2,6 +2,3 @@ export type InstanceApiEnvironment = {
2
2
  baseUrl: string
3
3
  }
4
4
 
5
- export const getInstanceFromEnv = (): string | undefined => {
6
- return process.env.CONTEMBER_API_URL || process.env.CONTEMBER_INSTANCE
7
- }
@@ -1,4 +1,4 @@
1
- import { getInstanceFromEnv, InstanceApiEnvironment } from './environment'
1
+ import { InstanceApiEnvironment } from './environment'
2
2
  import { Workspace } from '@contember/cli-common'
3
3
  import prompts from 'prompts'
4
4
 
@@ -21,7 +21,7 @@ export const interactiveResolveInstanceEnvironmentFromInput = async (
21
21
  ): Promise<InstanceApiEnvironment> => {
22
22
  let instanceName =
23
23
  instance ||
24
- getInstanceFromEnv() ||
24
+ workspace.env.apiUrl ||
25
25
  (
26
26
  await prompts({
27
27
  type: 'text',
@@ -0,0 +1,82 @@
1
+ import { Model, Schema } from '@contember/schema'
2
+ import { ReferencesMap } from '../../schema/collectReferences'
3
+ import { formatEntityAnchor, formatFieldAnchor } from './utils'
4
+ import { SinglePermission } from './SinglePermission'
5
+ import { FieldType } from './FieldType'
6
+ import { FieldPermissions } from './FieldPermissions'
7
+ import { OnDelete } from './OnDelete'
8
+ import { Fragment, h } from 'preact'
9
+
10
+ interface EntityInfoProps {
11
+ entity: Model.Entity
12
+ schema: Schema
13
+ references: ReferencesMap
14
+ }
15
+
16
+ export const EntityInfo = ({ entity, schema, references }: EntityInfoProps) => (
17
+ <div class={'m-4 border border-gray-400 rounded-lg group'}
18
+ id={formatEntityAnchor(entity.name)}>
19
+ <div class={'font-bold text-gray-900 py-2 px-4 border-b border-gray-400 group-target:bg-yellow-100 rounded-t-lg'}>
20
+ {entity.name}
21
+ </div>
22
+ <div class={'px-2 py-2 flex flex-wrap '}>
23
+ <div>
24
+ <table>
25
+ <thead>
26
+ <tr>
27
+ <th class={'text-left px-2 text-gray-500 font-normal w-[180px] max-w-[180px]'}>Name</th>
28
+ <th class={'text-left px-2 text-gray-500 font-normal w-[340px] max-w-[340px]'}>Type</th>
29
+ {Object.keys(schema.acl.roles).map(role => <th
30
+ class={'text-left px-2 text-gray-500 font-normal w-[120px] max-w-[120px] text-xs truncate text-sm'}>{role}</th>)}
31
+ </tr>
32
+ </thead>
33
+ <tr>
34
+ <td></td>
35
+ <td></td>
36
+ {Object.values(schema.acl.roles).map(role => <td class={'px-2'}>
37
+ <SinglePermission value={'D'}
38
+ predicate={role.entities[entity.name]?.operations.delete} />
39
+ </td>)}
40
+ </tr>
41
+ {Object.values(entity.fields).map(field => (
42
+ <tr class={'even:bg-gray-100 target:bg-yellow-100'}
43
+ id={formatFieldAnchor(entity.name, field.name)}>
44
+ <td class={'px-2 font-mono truncate w-[180px] max-w-[180px]'} title={field.name}>
45
+ {field.name}
46
+ </td>
47
+ <td class={'text-gray-800 px-2 font-mono w-[420px] max-w-[420px]'}>
48
+ <FieldType field={field} />
49
+ </td>
50
+ {Object.values(schema.acl.roles).map(role => <td class={'px-2'}>
51
+ <FieldPermissions field={field.name}
52
+ entityPermissions={role.entities[entity.name]} />
53
+ </td>)}
54
+ </tr>
55
+ ))}
56
+
57
+ </table>
58
+ </div>
59
+ <div class={'px-2 py-2 w-1/2'}>
60
+ <h3 class={'text-lg text-gray-600'}>On delete behaviour:</h3>
61
+ <p class={'text-gray-500 text-sm'}>
62
+ (What happens when you attempt to delete entity {entity.name})</p>
63
+ <div class={'mt-2 text-sm '}>
64
+ <OnDelete references={references} entity={entity} />
65
+ </div>
66
+ </div>
67
+ {false && <div class={'px-2 py-2 w-1/2'}>
68
+ <h3 class={'text-lg text-gray-600'}>ACL predicates:</h3>
69
+ <div class={'mt-2 text-sm '}>
70
+ {Object.entries(schema.acl.roles).map(([roleName, role]) =>
71
+ Object.entries(role.entities[entity.name]?.predicates ?? {}).map(([predicateName, predicate]) => (
72
+ <Fragment>
73
+ <h4>{roleName}: {predicateName}</h4>
74
+ <code>{JSON.stringify(predicate)}</code>
75
+ </Fragment>
76
+ )),
77
+ )}
78
+ </div>
79
+ </div>}
80
+ </div>
81
+ </div>
82
+ )
@@ -0,0 +1,6 @@
1
+ import { h } from 'preact'
2
+ import { formatEntityAnchor } from './utils'
3
+
4
+ export const EntityLink = ({ entity }: { entity: string }) => (
5
+ <a href={`#${formatEntityAnchor(entity)}`} class={'text-blue-500 hover:underline'}>{entity}</a>
6
+ )
@@ -0,0 +1,10 @@
1
+ import { Fragment, h } from 'preact'
2
+ import { EntityLink } from './EntityLink'
3
+ import { formatFieldAnchor } from './utils'
4
+
5
+ export const FieldLink = ({ entity, field, noEntityLabel }: { entity: string; field: string; noEntityLabel?: boolean }) => (
6
+ <Fragment>
7
+ {noEntityLabel ? '' : <Fragment><EntityLink entity={entity} />.</Fragment>}
8
+ <a href={`#${formatFieldAnchor(entity, field)}`} class={'text-blue-500 hover:underline'}>{field}</a>
9
+ </Fragment>
10
+ )
@@ -0,0 +1,14 @@
1
+ import { Fragment, h } from 'preact'
2
+ import { Acl } from '@contember/schema'
3
+ import { SinglePermission } from './SinglePermission'
4
+
5
+ export const FieldPermissions = ({ entityPermissions, field }: { entityPermissions?: Acl.EntityPermissions; field: string }) => {
6
+ const readPermissions = entityPermissions?.operations.read?.[field]
7
+ const updatePermissions = entityPermissions?.operations.update?.[field]
8
+ const createPermissions = entityPermissions?.operations.create?.[field]
9
+ return <Fragment>
10
+ <SinglePermission value={'C'} predicate={createPermissions} />
11
+ <SinglePermission value={'R'} predicate={readPermissions} />
12
+ <SinglePermission value={'U'} predicate={updatePermissions} />
13
+ </Fragment>
14
+ }
@@ -0,0 +1,54 @@
1
+ import { Model } from '@contember/schema'
2
+ import { h, Fragment } from 'preact'
3
+ import { EntityLink } from './EntityLink'
4
+ import { FieldLink } from './FieldLink'
5
+
6
+ export const FieldType = ({ field }: { field: Model.AnyField }) => {
7
+ if (field.type === 'ManyHasMany') {
8
+ return <>
9
+ <EntityLink entity={field.target}/>[]{' '}
10
+ <TargetField field={field}/> (m:n)
11
+ </>
12
+
13
+ } else if (field.type === 'OneHasMany') {
14
+ return <>
15
+ <EntityLink entity={field.target}/>[]{' '}
16
+ <TargetField field={field}/> (1:n)
17
+ </>
18
+
19
+ } else if (field.type === 'OneHasOne') {
20
+ return <>
21
+ <NullableMark field={field} />
22
+ <EntityLink entity={field.target}/>{' '}
23
+ <TargetField field={field}/> (1:1)
24
+ </>
25
+
26
+ } else if (field.type === 'ManyHasOne') {
27
+ return <>
28
+ <NullableMark field={field} />
29
+ <EntityLink entity={field.target}/>{' '}
30
+ <TargetField field={field}/> (m:1)
31
+ </>
32
+
33
+ } else {
34
+ return <>
35
+ <NullableMark field={field} />
36
+ {field.type}
37
+ </>
38
+
39
+ }
40
+ }
41
+
42
+ const TargetField = ({ field }: {field: Model.AnyRelation}) => {
43
+ if ('ownedBy' in field) {
44
+ return <FieldLink entity={field.target} field={field.ownedBy} noEntityLabel />
45
+ }
46
+ if (field.inversedBy) {
47
+ return <FieldLink entity={field.target} field={field.inversedBy} noEntityLabel />
48
+ }
49
+ return null
50
+ }
51
+
52
+ const NullableMark = (props: { field: Model.NullableRelation | Model.AnyColumn }) => {
53
+ return props.field.nullable ? <span class={'font-bold color-black'}>? </span> : null
54
+ }
@@ -0,0 +1,72 @@
1
+ import { h, Fragment } from 'preact'
2
+ import { Model } from '@contember/schema'
3
+ import { ReferenceMapEntry, ReferencesMap } from '../../schema/collectReferences'
4
+ import { FieldLink } from './FieldLink'
5
+ import { EntityLink } from './EntityLink'
6
+
7
+ const sortReferences = (a: ReferenceMapEntry, b: ReferenceMapEntry) =>
8
+ a.owningRelation.joiningColumn.onDelete.localeCompare(b.owningRelation.joiningColumn.onDelete)
9
+ || a.owningEntity.name.localeCompare(b.owningEntity.name)
10
+ || a.owningRelation.name.localeCompare(b.owningRelation.name)
11
+
12
+ export const OnDelete = ({ references, entity, visited = [] }: { references: ReferencesMap; entity: Model.Entity; visited?: Model.Entity[] }) => {
13
+ const referencedFrom = references[entity.name]
14
+ if (referencedFrom.length === 0) {
15
+ if (visited.length !== 0) {
16
+ return null
17
+ }
18
+ return (
19
+ <span class={'bg-green-200 text-gray-600 p-1 rounded'}>
20
+ This entity is not referenced from any other entity.
21
+ </span>
22
+ )
23
+ }
24
+
25
+ if (visited.includes(entity)) {
26
+ return (
27
+ <ul class={'ml-4'}>
28
+ <li class={'italic'}>(recursion)</li>
29
+ </ul>
30
+ )
31
+ }
32
+
33
+ return (
34
+ <ul class={visited.length > 0 ? 'ml-4' : ''}>
35
+ {referencedFrom
36
+ .sort(sortReferences)
37
+ .map(it => {
38
+ const inverseDescr = it.targetRelation
39
+ ? <>see <FieldLink entity={entity.name} field={it.targetRelation.name} /></>
40
+ : 'no inverse side'
41
+ if (it.owningRelation.joiningColumn.onDelete === 'restrict') {
42
+ return (
43
+ <li>
44
+ <span class={'bg-black text-white px-1'}>Fails</span> when referenced from
45
+ {' '}<FieldLink entity={it.owningEntity.name} field={it.owningRelation.name} />
46
+ {' '}({inverseDescr})
47
+ </li>
48
+ )
49
+ }
50
+ if (it.owningRelation.joiningColumn.onDelete === 'set null') {
51
+ return (
52
+ <li>
53
+ <span class={'bg-blue-400 text-white px-1'}>Sets null</span> at
54
+ {' '}<FieldLink entity={it.owningEntity.name} field={it.owningRelation.name} />
55
+ {' '}({inverseDescr})
56
+ </li>
57
+ )
58
+ }
59
+ if (it.owningRelation.joiningColumn.onDelete === 'cascade') {
60
+ return (
61
+ <li>
62
+ <span class={'bg-red-400 text-white px-1'}>Deletes</span> <EntityLink entity={it.owningEntity.name} />
63
+ {' '}connected using
64
+ {' '}<FieldLink entity={it.owningEntity.name} field={it.owningRelation.name} noEntityLabel />
65
+ {' '}({inverseDescr})
66
+ <OnDelete entity={it.owningEntity} visited={[...visited, entity]} references={references} />
67
+ </li>
68
+ )
69
+ }
70
+ })}
71
+ </ul>)
72
+ }
@@ -0,0 +1,48 @@
1
+ import { Schema } from '@contember/schema'
2
+ import { h } from 'preact'
3
+ import { collectReferences } from '../../schema/collectReferences'
4
+ import { EntityLink } from './EntityLink'
5
+ import { EntityInfo } from './EntityInfo'
6
+
7
+
8
+ export const ProjectInfo = (props: { schema: Schema; projectName: string }) => {
9
+ const references = collectReferences(props.schema.model)
10
+
11
+ return (
12
+ <div class={'w-full'}>
13
+ <div class={'bg-gray-100 p-6 border-b border-gray-400'}>
14
+ <h1 class={'font-bold text-xl mb-1'}>{props.projectName}</h1>
15
+ <div class={'flex flex-column gap-4'}>
16
+ <div>
17
+ <h2 class={'font-semibold text-gray-500 mb-1'}>Entities</h2>
18
+ <ul class={'text-sm'}>
19
+ {Object.values(props.schema.model.entities).map(entity => (
20
+ <li>
21
+ <EntityLink entity={entity.name} />
22
+ </li>
23
+ ))}
24
+ </ul>
25
+ </div>
26
+ <div>
27
+ <h2 class={'font-semibold text-gray-500 mb-1'}>Enums</h2>
28
+ <ul class={'text-sm'}>
29
+ {Object.entries(props.schema.model.enums).map(([name, values]) => (
30
+ <li class={'mb-2'}>
31
+ {name} <br />
32
+ <span class={'text-gray-700 ml-4'}>
33
+ {values.join(', ')}
34
+ </span>
35
+ </li>
36
+ ))}
37
+ </ul>
38
+ </div>
39
+ </div>
40
+
41
+
42
+ </div>
43
+ {Object.values(props.schema.model.entities).map(entity => (
44
+ <EntityInfo entity={entity} schema={props.schema} references={references}/>
45
+ ))}
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,13 @@
1
+ import { h } from 'preact'
2
+ import { Acl } from '@contember/schema'
3
+
4
+ export const SinglePermission = ({ predicate, value }: { predicate?: Acl.Predicate; value: string }) => {
5
+ if (!predicate) {
6
+ return <span class={'bg-red-500 text-white font-semibold px-0.5'}>{value}</span>
7
+ }
8
+ if (predicate === true) {
9
+ return <span class={'bg-green-600 text-white font-semibold px-0.5'}>{value}</span>
10
+ }
11
+ return <span class={'bg-yellow-400 text-black font-semibold px-0.5'} title={predicate}>{value}</span>
12
+ }
13
+
@@ -0,0 +1,3 @@
1
+ export const formatEntityAnchor = (name: string) => `entity-${name}`
2
+ export const formatFieldAnchor = (entity: string, field: string) => `field-${entity}--${field}`
3
+
File without changes
@@ -0,0 +1,26 @@
1
+ import { Schema } from '@contember/schema'
2
+ import { h } from 'preact'
3
+ import render from 'preact-render-to-string'
4
+ import { ProjectInfo } from './components/ProjectInfo'
5
+ import { readFile } from 'fs/promises'
6
+
7
+ export const renderProjectInfoHtml = async (schema: Schema, projectName: string) => {
8
+ const html = render(<ProjectInfo schema={schema} projectName={projectName} />)
9
+
10
+ const css = await readFile(__dirname + '/resources/out.css')
11
+ return `<!DOCTYPE html>
12
+ <html lang="en">
13
+ <head>
14
+ <meta charset="UTF-8">
15
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
+ <style>${css}</style>
17
+ <title>${projectName}</title>
18
+ </head>
19
+ <body>
20
+ ${html}
21
+ </body>
22
+ </html>
23
+ `
24
+
25
+ }
26
+
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,40 @@
1
+ import { Model } from '@contember/schema'
2
+
3
+ export type ReferenceMapEntry = {
4
+ owningEntity: Model.Entity
5
+ owningRelation: Model.OneHasOneOwningRelation | Model.ManyHasOneRelation
6
+ targetEntity: Model.Entity
7
+ targetRelation: Model.OneHasOneInverseRelation | Model.OneHasManyRelation | null
8
+ }
9
+ export type ReferencesMap = Record<string, ReferenceMapEntry[]>
10
+
11
+ export const collectReferences = (model: Model.Schema): ReferencesMap => {
12
+ const entityReferences: ReferencesMap = Object.fromEntries(Object.keys(model.entities).map(it => [it, []]))
13
+ for (const entity of Object.values(model.entities)) {
14
+ for (const field of Object.values(entity.fields)) {
15
+ switch (field.type) {
16
+ case 'OneHasOne':
17
+ const targetEntity = model.entities[field.target]
18
+ if (!('ownedBy' in field)) {
19
+ entityReferences[field.target].push({
20
+ owningEntity: entity,
21
+ owningRelation: field,
22
+ targetEntity: targetEntity,
23
+ targetRelation: field.inversedBy ? targetEntity.fields[field.inversedBy] as Model.OneHasOneInverseRelation : null,
24
+ })
25
+ }
26
+ break
27
+ case 'ManyHasOne':
28
+ const targetEntity2 = model.entities[field.target]
29
+ entityReferences[field.target].push({
30
+ owningEntity: entity,
31
+ owningRelation: field,
32
+ targetEntity: targetEntity2,
33
+ targetRelation: field.inversedBy ? targetEntity2.fields[field.inversedBy] as Model.OneHasManyRelation : null,
34
+ })
35
+ break
36
+ }
37
+ }
38
+ }
39
+ return entityReferences
40
+ }
@@ -1,6 +1,7 @@
1
1
  import prompts from 'prompts'
2
2
  import { GraphQLClient } from 'graphql-request'
3
3
  import { InstanceApiEnvironment } from './instance'
4
+ import { Workspace } from '@contember/cli-common'
4
5
 
5
6
  const validatePassword = (password: string) =>
6
7
  password.length < 6 ? 'Password must contain at least 6 characters' : true
@@ -15,9 +16,9 @@ export const createTenantApiUrl = (url: string) => {
15
16
  return url + '/tenant'
16
17
  }
17
18
 
18
- export const interactiveResolveLoginToken = async (instance: InstanceApiEnvironment) => {
19
- if (process.env.CONTEMBER_LOGIN_TOKEN) {
20
- return process.env.CONTEMBER_LOGIN_TOKEN
19
+ export const interactiveResolveLoginToken = async (workspace: Workspace) => {
20
+ if (workspace.env.loginToken) {
21
+ return workspace.env.loginToken
21
22
  }
22
23
  const { loginToken } = await prompts({
23
24
  type: 'text',
@@ -83,12 +84,19 @@ export const interactiveAskForCredentials = async (): Promise<{ email: string; p
83
84
  }
84
85
 
85
86
  export const interactiveResolveApiToken = async ({
87
+ workspace,
86
88
  instance,
89
+ apiToken,
87
90
  }: {
91
+ workspace: Workspace
88
92
  instance: InstanceApiEnvironment
93
+ apiToken?: string
89
94
  }): Promise<string> => {
90
- if (process.env.CONTEMBER_API_TOKEN) {
91
- return process.env.CONTEMBER_API_TOKEN
95
+ if (apiToken) {
96
+ return apiToken
97
+ }
98
+ if (workspace.env.apiToken) {
99
+ return workspace.env.apiToken
92
100
  }
93
101
  const { strategy } = await prompts({
94
102
  type: 'select',
@@ -100,7 +108,7 @@ export const interactiveResolveApiToken = async ({
100
108
  ],
101
109
  })
102
110
  if (strategy === 'signin') {
103
- const loginToken = await interactiveResolveLoginToken(instance)
111
+ const loginToken = await interactiveResolveLoginToken(workspace)
104
112
  return (await interactiveSignIn({ apiUrl: createTenantApiUrl(instance.baseUrl), loginToken, expiration: 60 * 5 }))
105
113
  .sessionToken
106
114
  }
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ content: ['./src/utils/project/components/**/*.tsx'],
3
+ theme: {
4
+ extend: {},
5
+ },
6
+ plugins: [],
7
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/utils/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAM1C,eAAO,MAAM,UAAU,YAAmB,OAAO,KAAG,QAAQ,MAAM,CAmBjE,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/utils/project.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,iDAAkC;AAClC,+BAA2B;AAC3B,6BAA4B;AAC5B,0DAAoD;AAE7C,MAAM,UAAU,GAAG,KAAK,EAAE,OAAgB,EAAmB,EAAE;IACrE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;QACpC,WAAW,EAAE,CAAC,IAAA,WAAI,EAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC/C,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,IAAI;QACjB,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,CAAC,kBAAkB,EAAE,OAAO,EAAE,mBAAmB,CAAC;KAC5D,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC9C,MAAM,EAAE,GAAG,IAAI,YAAM,CAAC;QACrB,OAAO,EAAE;YACR,OAAO,EAAE,CAAC,GAAG,CAAC;SACd;KACD,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,CAAA;IAC9C,gDAAgD;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;IACtD,OAAO,IAAA,yBAAU,EAAC,WAAW,CAAC,CAAA;AAC/B,CAAC,CAAA;AAnBY,QAAA,UAAU,cAmBtB"}