@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.
- package/dist/src/commands/deploy/DeployCommand.d.ts +3 -1
- package/dist/src/commands/deploy/DeployCommand.d.ts.map +1 -1
- package/dist/src/commands/deploy/DeployCommand.js +83 -10
- package/dist/src/commands/deploy/DeployCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationAmendCommand.js +2 -2
- package/dist/src/commands/migrations/MigrationAmendCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.js +3 -3
- package/dist/src/commands/migrations/MigrationDiffCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationExecuteCommand.js +1 -1
- package/dist/src/commands/migrations/MigrationExecuteCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationExecuteHelper.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationExecuteHelper.js +3 -0
- package/dist/src/commands/migrations/MigrationExecuteHelper.js.map +1 -1
- package/dist/src/commands/migrations/MigrationStatusCommand.js +1 -1
- package/dist/src/commands/migrations/MigrationStatusCommand.js.map +1 -1
- package/dist/src/commands/migrations/SystemApiClientResolver.js +1 -1
- package/dist/src/commands/migrations/SystemApiClientResolver.js.map +1 -1
- package/dist/src/commands/project/ProjectGenerateDocumentation.d.ts +13 -0
- package/dist/src/commands/project/ProjectGenerateDocumentation.d.ts.map +1 -0
- package/dist/src/commands/project/ProjectGenerateDocumentation.js +39 -0
- package/dist/src/commands/project/ProjectGenerateDocumentation.js.map +1 -0
- package/dist/src/commands/project/ProjectPrintSchemaCommand.d.ts +1 -1
- package/dist/src/commands/project/ProjectPrintSchemaCommand.d.ts.map +1 -1
- package/dist/src/commands/project/ProjectPrintSchemaCommand.js +15 -7
- package/dist/src/commands/project/ProjectPrintSchemaCommand.js.map +1 -1
- package/dist/src/commands/project/ProjectValidateCommand.js +2 -2
- package/dist/src/commands/project/ProjectValidateCommand.js.map +1 -1
- package/dist/src/commands/project/index.d.ts +1 -0
- package/dist/src/commands/project/index.d.ts.map +1 -1
- package/dist/src/commands/project/index.js +1 -0
- package/dist/src/commands/project/index.js.map +1 -1
- package/dist/src/commands/tenant/CreateApiKeyCommand.js +1 -1
- package/dist/src/commands/tenant/CreateApiKeyCommand.js.map +1 -1
- package/dist/src/commands/tenant/InviteCommand.js +1 -1
- package/dist/src/commands/tenant/InviteCommand.js.map +1 -1
- package/dist/src/commands/tenant/ResetPasswordCommand.js +1 -1
- package/dist/src/commands/tenant/ResetPasswordCommand.js.map +1 -1
- package/dist/src/commands/tenant/SignInCommand.js +1 -1
- package/dist/src/commands/tenant/SignInCommand.js.map +1 -1
- package/dist/src/run.js +1 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/instance/environment.d.ts +0 -1
- package/dist/src/utils/instance/environment.d.ts.map +1 -1
- package/dist/src/utils/instance/environment.js +0 -5
- package/dist/src/utils/instance/environment.js.map +1 -1
- package/dist/src/utils/instance/interactiveUtils.d.ts.map +1 -1
- package/dist/src/utils/instance/interactiveUtils.js +1 -2
- package/dist/src/utils/instance/interactiveUtils.js.map +1 -1
- package/dist/src/utils/project/components/EntityInfo.d.ts +11 -0
- package/dist/src/utils/project/components/EntityInfo.d.ts.map +1 -0
- package/dist/src/utils/project/components/EntityInfo.js +54 -0
- package/dist/src/utils/project/components/EntityInfo.js.map +1 -0
- package/dist/src/utils/project/components/EntityLink.d.ts +5 -0
- package/dist/src/utils/project/components/EntityLink.d.ts.map +1 -0
- package/dist/src/utils/project/components/EntityLink.js +8 -0
- package/dist/src/utils/project/components/EntityLink.js.map +1 -0
- package/dist/src/utils/project/components/FieldLink.d.ts +7 -0
- package/dist/src/utils/project/components/FieldLink.d.ts.map +1 -0
- package/dist/src/utils/project/components/FieldLink.js +13 -0
- package/dist/src/utils/project/components/FieldLink.js.map +1 -0
- package/dist/src/utils/project/components/FieldPermissions.d.ts +7 -0
- package/dist/src/utils/project/components/FieldPermissions.d.ts.map +1 -0
- package/dist/src/utils/project/components/FieldPermissions.js +17 -0
- package/dist/src/utils/project/components/FieldPermissions.js.map +1 -0
- package/dist/src/utils/project/components/FieldType.d.ts +6 -0
- package/dist/src/utils/project/components/FieldType.d.ts.map +1 -0
- package/dist/src/utils/project/components/FieldType.js +59 -0
- package/dist/src/utils/project/components/FieldType.js.map +1 -0
- package/dist/src/utils/project/components/OnDelete.d.ts +9 -0
- package/dist/src/utils/project/components/OnDelete.d.ts.map +1 -0
- package/dist/src/utils/project/components/OnDelete.js +70 -0
- package/dist/src/utils/project/components/OnDelete.js.map +1 -0
- package/dist/src/utils/project/components/ProjectInfo.d.ts +7 -0
- package/dist/src/utils/project/components/ProjectInfo.d.ts.map +1 -0
- package/dist/src/utils/project/components/ProjectInfo.js +28 -0
- package/dist/src/utils/project/components/ProjectInfo.js.map +1 -0
- package/dist/src/utils/project/components/SinglePermission.d.ts +7 -0
- package/dist/src/utils/project/components/SinglePermission.d.ts.map +1 -0
- package/dist/src/utils/project/components/SinglePermission.js +15 -0
- package/dist/src/utils/project/components/SinglePermission.js.map +1 -0
- package/dist/src/utils/project/components/utils.d.ts +3 -0
- package/dist/src/utils/project/components/utils.d.ts.map +1 -0
- package/dist/src/utils/project/components/utils.js +8 -0
- package/dist/src/utils/project/components/utils.js.map +1 -0
- package/dist/src/utils/{project.d.ts → project/loadSchema.d.ts} +1 -1
- package/dist/src/utils/project/loadSchema.d.ts.map +1 -0
- package/dist/src/utils/{project.js → project/loadSchema.js} +1 -1
- package/dist/src/utils/project/loadSchema.js.map +1 -0
- package/dist/src/utils/project/projectDescribe.d.ts +3 -0
- package/dist/src/utils/project/projectDescribe.d.ts.map +1 -0
- package/dist/src/utils/project/projectDescribe.js +29 -0
- package/dist/src/utils/project/projectDescribe.js.map +1 -0
- package/dist/src/utils/project/resources/out.css +759 -0
- package/dist/src/utils/schema/collectReferences.d.ts +10 -0
- package/dist/src/utils/schema/collectReferences.d.ts.map +1 -0
- package/dist/src/utils/schema/collectReferences.js +35 -0
- package/dist/src/utils/schema/collectReferences.js.map +1 -0
- package/dist/src/utils/tenant.d.ts +5 -2
- package/dist/src/utils/tenant.d.ts.map +1 -1
- package/dist/src/utils/tenant.js +10 -7
- package/dist/src/utils/tenant.js.map +1 -1
- package/package.json +17 -11
- package/postcss.config.js +6 -0
- package/src/commands/deploy/DeployCommand.ts +88 -14
- package/src/commands/migrations/MigrationAmendCommand.ts +1 -1
- package/src/commands/migrations/MigrationDiffCommand.ts +2 -2
- package/src/commands/migrations/MigrationExecuteCommand.ts +1 -1
- package/src/commands/migrations/MigrationExecuteHelper.ts +3 -0
- package/src/commands/migrations/MigrationStatusCommand.ts +1 -1
- package/src/commands/migrations/SystemApiClientResolver.ts +1 -1
- package/src/commands/project/ProjectGenerateDocumentation.ts +44 -0
- package/src/commands/project/ProjectPrintSchemaCommand.ts +13 -7
- package/src/commands/project/ProjectValidateCommand.ts +1 -1
- package/src/commands/project/index.ts +1 -0
- package/src/commands/tenant/CreateApiKeyCommand.ts +1 -1
- package/src/commands/tenant/InviteCommand.ts +1 -1
- package/src/commands/tenant/ResetPasswordCommand.ts +1 -1
- package/src/commands/tenant/SignInCommand.ts +1 -1
- package/src/run.ts +2 -0
- package/src/tsconfig.json +5 -1
- package/src/utils/instance/environment.ts +0 -3
- package/src/utils/instance/interactiveUtils.ts +2 -2
- package/src/utils/project/components/EntityInfo.tsx +82 -0
- package/src/utils/project/components/EntityLink.tsx +6 -0
- package/src/utils/project/components/FieldLink.tsx +10 -0
- package/src/utils/project/components/FieldPermissions.tsx +14 -0
- package/src/utils/project/components/FieldType.tsx +54 -0
- package/src/utils/project/components/OnDelete.tsx +72 -0
- package/src/utils/project/components/ProjectInfo.tsx +48 -0
- package/src/utils/project/components/SinglePermission.tsx +13 -0
- package/src/utils/project/components/utils.ts +3 -0
- package/src/utils/{project.ts → project/loadSchema.ts} +0 -0
- package/src/utils/project/projectDescribe.tsx +26 -0
- package/src/utils/project/resources/style.css +3 -0
- package/src/utils/schema/collectReferences.ts +40 -0
- package/src/utils/tenant.ts +14 -6
- package/tailwind.config.js +7 -0
- package/dist/src/utils/project.d.ts.map +0 -1
- 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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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,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
|
+
|
|
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,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
|
+
}
|
package/src/utils/tenant.ts
CHANGED
|
@@ -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 (
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
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 (
|
|
91
|
-
return
|
|
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(
|
|
111
|
+
const loginToken = await interactiveResolveLoginToken(workspace)
|
|
104
112
|
return (await interactiveSignIn({ apiUrl: createTenantApiUrl(instance.baseUrl), loginToken, expiration: 60 * 5 }))
|
|
105
113
|
.sessionToken
|
|
106
114
|
}
|
|
@@ -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"}
|