Initial implementation
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div
|
||||
class="border py-1 px-2 rounded"
|
||||
:class="selected ? 'bg-blue-100' : 'bg-foundation'"
|
||||
>
|
||||
<div class="flex flex-col justify-between">
|
||||
<button
|
||||
class="flex flex-row justify-between items-center"
|
||||
@click="$emit('select', folderContent)"
|
||||
>
|
||||
<div class="text-body-xs text-foreground">
|
||||
{{ folderContent.attributes.displayName || folderContent.attributes.name }}
|
||||
</div>
|
||||
<div class="flex flex-row gap-2 items-center" :class="expanded ? 'mb-1' : ''">
|
||||
<FormButton
|
||||
v-if="folderContent.storageUrn"
|
||||
:icon-left="ArrowDownTrayIcon"
|
||||
hide-text
|
||||
color="outline"
|
||||
size="sm"
|
||||
@click.stop="$emit('download', folderContent)"
|
||||
>
|
||||
Details
|
||||
</FormButton>
|
||||
|
||||
<FormButton
|
||||
size="sm"
|
||||
hide-text
|
||||
:icon-left="!expanded ? ChevronDownIcon : ChevronUpIcon"
|
||||
color="outline"
|
||||
@click.stop="expanded = !expanded"
|
||||
></FormButton>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div v-if="expanded" class="space-y-1">
|
||||
<hr />
|
||||
<div class="text-xs italic">Type: {{ folderContent.type }}</div>
|
||||
<div class="text-xs text-gray-500 break-all">
|
||||
Lineage ID: {{ folderContent.id }}
|
||||
</div>
|
||||
<div
|
||||
v-if="folderContent.latestVersionId"
|
||||
class="text-xs text-blue-500 break-all"
|
||||
>
|
||||
Version ID: {{ folderContent.latestVersionId }}
|
||||
</div>
|
||||
<div v-if="folderContent.storageUrn" class="text-xs text-green-600 break-all">
|
||||
Storage URN: {{ folderContent.storageUrn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon
|
||||
} from '@heroicons/vue/20/solid'
|
||||
import type { AccItem } from '~/lib/acc/types'
|
||||
|
||||
defineProps<{
|
||||
folderContent: AccItem
|
||||
loading: boolean
|
||||
selected: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'download', item: AccItem): void
|
||||
(e: 'select', item: AccItem): void
|
||||
}>()
|
||||
|
||||
const expanded = ref(false)
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex text-body-xs text-foreground font-medium">Files</div>
|
||||
<div v-if="loading" class="text-xs italic">Loading files...</div>
|
||||
<div v-else-if="folderContents.length" class="flex flex-col space-y-0.5">
|
||||
<div v-for="item in revitContents" :key="item.id">
|
||||
<ProjectPageAccFileItem
|
||||
:folder-content="item"
|
||||
:loading="loading"
|
||||
:selected="item.id === selectedFolderContent?.id"
|
||||
@download="(i) => $emit('download', i)"
|
||||
@select="(i) => $emit('select', i)"
|
||||
></ProjectPageAccFileItem>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-xs italic">No files found in this folder.</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AccItem } from '~/lib/acc/types'
|
||||
|
||||
const props = defineProps<{
|
||||
selectedFolderContent: AccItem | undefined
|
||||
folderContents: AccItem[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'download', item: AccItem): void
|
||||
(e: 'select', item: AccItem): void
|
||||
}>()
|
||||
|
||||
const revitContents = computed(() =>
|
||||
props.folderContents.filter((fc) =>
|
||||
(fc.attributes.name || fc.attributes.displayName).includes('.rvt')
|
||||
)
|
||||
)
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<FormSelectBase
|
||||
v-model="selectedHub"
|
||||
name="accHubs"
|
||||
label="Hubs"
|
||||
show-label
|
||||
:items="hubs"
|
||||
size="base"
|
||||
color="foundation"
|
||||
placeholder="Select hub"
|
||||
@update:model-value="handleHubChange"
|
||||
>
|
||||
<template #something-selected="{ value }">
|
||||
{{ isArray(value) ? value[0].attributes.name : value.attributes.name }}
|
||||
</template>
|
||||
<template #option="{ item }">
|
||||
{{ item.attributes.name }}
|
||||
</template>
|
||||
</FormSelectBase>
|
||||
|
||||
<div v-if="!loading && hubs.length == 0" class="text-xs italic">No hubs found.</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isArray } from 'lodash-es'
|
||||
import type { AccHub } from '~/lib/acc/types'
|
||||
|
||||
const props = defineProps<{
|
||||
hubs: AccHub[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'hub-selected', hub: AccHub): void
|
||||
}>()
|
||||
|
||||
const handleHubChange = (newHub: AccHub | AccHub[] | undefined) => {
|
||||
// is array not likely but make TS happy
|
||||
if (!newHub || isArray(newHub)) {
|
||||
return
|
||||
}
|
||||
emits('hub-selected', newHub)
|
||||
}
|
||||
|
||||
const selectedHub = ref<AccHub>()
|
||||
|
||||
watch(
|
||||
() => props.hubs,
|
||||
(newHubs) => {
|
||||
if (newHubs.length > 0) {
|
||||
selectedHub.value = newHubs[0]
|
||||
emits('hub-selected', newHubs[0])
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<FormSelectBase
|
||||
v-model="selectedProject"
|
||||
name="accProjects"
|
||||
label="Projects"
|
||||
show-label
|
||||
:items="projects"
|
||||
size="base"
|
||||
color="foundation"
|
||||
placeholder="Select hub"
|
||||
@update:model-value="handleProjectChange"
|
||||
>
|
||||
<template #something-selected="{ value }">
|
||||
{{ isArray(value) ? value[0].attributes.name : value.attributes.name }}
|
||||
</template>
|
||||
<template #option="{ item }">
|
||||
{{ item.attributes.name }}
|
||||
</template>
|
||||
</FormSelectBase>
|
||||
|
||||
<div v-if="!loading && projects.length == 0" class="text-xs italic">
|
||||
No projects found.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isArray } from 'lodash-es'
|
||||
import type { AccProject } from '~/lib/acc/types'
|
||||
|
||||
const props = defineProps<{
|
||||
hubId: string
|
||||
projects: AccProject[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'project-selected', hubId: string, projectId: string): void
|
||||
}>()
|
||||
|
||||
const selectedProject = ref<AccProject>()
|
||||
|
||||
const handleProjectChange = (newProject: AccProject | AccProject[] | undefined) => {
|
||||
// is array not likely but make TS happy
|
||||
if (!newProject || isArray(newProject)) {
|
||||
return
|
||||
}
|
||||
emits('project-selected', props.hubId, newProject.id)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.projects,
|
||||
(newProjects) => {
|
||||
if (newProjects.length > 0) {
|
||||
selectedProject.value = newProjects[0]
|
||||
emits('project-selected', props.hubId, newProjects[0].id)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<CommonBadge
|
||||
:color-classes="
|
||||
[runStatusClasses(status), 'shrink-0 grow-0 text-foreground'].join(' ')
|
||||
"
|
||||
>
|
||||
{{ status.toUpperCase() }}
|
||||
</CommonBadge>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AccSyncItemStatus } from '~/lib/acc/types'
|
||||
|
||||
defineProps<{
|
||||
status: AccSyncItemStatus
|
||||
}>()
|
||||
|
||||
const runStatusClasses = (run: AccSyncItemStatus) => {
|
||||
const classParts = ['w-24 justify-center']
|
||||
|
||||
switch (run) {
|
||||
case 'syncing':
|
||||
classParts.push('bg-info-lighter')
|
||||
break
|
||||
case 'paused':
|
||||
classParts.push('bg-warning-lighter')
|
||||
break
|
||||
case 'failed':
|
||||
classParts.push('bg-danger-lighter')
|
||||
break
|
||||
case 'sync':
|
||||
classParts.push('bg-success-lighter')
|
||||
break
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="flex text-body-xs text-foreground font-medium">Sync items</div>
|
||||
<LayoutTable
|
||||
class="bg-foundation"
|
||||
:columns="[
|
||||
{ id: 'status', header: 'Status', classes: 'col-span-2' },
|
||||
{ id: 'accFileName', header: 'File name', classes: 'col-span-3' },
|
||||
{ id: 'modelName', header: 'Model name', classes: 'col-span-3' },
|
||||
{ id: 'createdBy', header: 'Created by', classes: 'col-span-3' }
|
||||
]"
|
||||
:items="syncs"
|
||||
>
|
||||
<template #status="{ item }">
|
||||
<ProjectPageAccSyncStatus :status="item.status" />
|
||||
</template>
|
||||
<template #accFileName="{ item }">
|
||||
{{ item.accItem.attributes.name || item.accItem.attributes.displayName }}
|
||||
</template>
|
||||
<template #modelName="{ item }">
|
||||
{{ item.modelName }}
|
||||
</template>
|
||||
<template #createdBy="{ item }">
|
||||
{{ item.createdBy }}
|
||||
</template>
|
||||
</LayoutTable>
|
||||
<FormButton
|
||||
color="outline"
|
||||
size="sm"
|
||||
:disabled="!isLoggedIn"
|
||||
:disabled-tooltip="'Log in required'"
|
||||
@click="showNewSyncDialog = true"
|
||||
>
|
||||
<template #default>
|
||||
<div v-tippy="isLoggedIn ? undefined : 'Log in required'">New sync</div>
|
||||
</template>
|
||||
</FormButton>
|
||||
<LayoutDialog v-model:open="showNewSyncDialog" title="Create new sync">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div v-if="step === 0">
|
||||
<ProjectPageAccHubs
|
||||
:hubs="hubs"
|
||||
:loading="loadingHubs"
|
||||
@hub-selected="onHubClick"
|
||||
/>
|
||||
|
||||
<ProjectPageAccProjects
|
||||
v-if="selectedHubId"
|
||||
:hub-id="selectedHubId"
|
||||
:projects="projects"
|
||||
:loading="loadingProjects"
|
||||
@project-selected="onProjectClick"
|
||||
/>
|
||||
|
||||
<ProjectPageAccFiles
|
||||
v-if="selectedProjectId"
|
||||
:folder-contents="folderContents"
|
||||
:selected-folder-content="selectedFolderContent"
|
||||
:loading="loadingFiles"
|
||||
@download="onDownloadClick"
|
||||
@select="onFileSelected"
|
||||
/>
|
||||
|
||||
<div class="flex flex-row justify-center mt-4 space-x-2">
|
||||
<FormButton size="sm" color="outline" @click="showNewSyncDialog = false">
|
||||
Cancel
|
||||
</FormButton>
|
||||
<FormButton size="sm" :disabled="!selectedFolderContent" @click="step++">
|
||||
Next
|
||||
</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="step === 1" class="flex flex-col justify-between space-y-2">
|
||||
<div>
|
||||
Selected ACC file:
|
||||
{{
|
||||
selectedFolderContent?.attributes.name ||
|
||||
selectedFolderContent?.attributes.displayName
|
||||
}}
|
||||
</div>
|
||||
<FormSelectBase
|
||||
v-model="selectedModel"
|
||||
:label="'Models'"
|
||||
:name="'accModelSelector'"
|
||||
show-label
|
||||
:items="models"
|
||||
mount-menu-on-body
|
||||
>
|
||||
<template #something-selected="{ value }">
|
||||
{{ isArray(value) ? value[0].name : value.name }}
|
||||
</template>
|
||||
<template #option="{ item }">
|
||||
{{ item.name }}
|
||||
</template>
|
||||
</FormSelectBase>
|
||||
<div class="flex flex-row justify-center mt-4 space-x-2">
|
||||
<FormButton size="sm" color="outline" @click="showNewSyncDialog = false">
|
||||
Cancel
|
||||
</FormButton>
|
||||
<FormButton size="sm" :disabled="!selectedModel" @click="addSync">
|
||||
Add
|
||||
</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
AccTokens,
|
||||
AccHub,
|
||||
AccProject,
|
||||
AccItem,
|
||||
AccSyncItem
|
||||
} from '~/lib/acc/types'
|
||||
import { ref, computed } from 'vue'
|
||||
import type {
|
||||
ProjectLatestModelsPaginationQueryVariables,
|
||||
ProjectPageLatestItemsModelItemFragment
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { latestModelsQuery } from '~/lib/projects/graphql/queries'
|
||||
import { isArray } from 'lodash-es'
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: string
|
||||
tokens: AccTokens | undefined
|
||||
syncs: AccSyncItem[]
|
||||
isLoggedIn: boolean
|
||||
}>()
|
||||
|
||||
const internalSyncs = computed(() => props.syncs)
|
||||
|
||||
const step = ref(0)
|
||||
|
||||
const showNewSyncDialog = ref(false)
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
|
||||
const tokens = computed(() => props.tokens)
|
||||
const hubs = ref<AccHub[]>([])
|
||||
const loadingHubs = ref(false)
|
||||
const selectedHub = ref<AccHub | null>(null)
|
||||
const selectedHubId = ref<string | null>(null)
|
||||
const folderUrn = ref<string | null>(null)
|
||||
const projects = ref<AccProject[]>([])
|
||||
const loadingProjects = ref(false)
|
||||
const selectedProjectId = ref<string | null>(null)
|
||||
const folderContents = ref<AccItem[]>([])
|
||||
const selectedFolderContent = ref<AccItem>()
|
||||
const loadingFiles = ref(false)
|
||||
|
||||
const searchText = ref<string>()
|
||||
|
||||
const selectedModel = ref<ProjectPageLatestItemsModelItemFragment>()
|
||||
|
||||
const latestModelsQueryVariables = computed(
|
||||
(): ProjectLatestModelsPaginationQueryVariables => {
|
||||
const shouldHaveFilter = searchText.value && searchText.value.length > 0
|
||||
return {
|
||||
projectId: props.projectId,
|
||||
filter: shouldHaveFilter
|
||||
? {
|
||||
search: searchText.value || null
|
||||
}
|
||||
: null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const { result: baseResult } = useQuery(
|
||||
latestModelsQuery,
|
||||
() => latestModelsQueryVariables.value
|
||||
)
|
||||
|
||||
const models = computed(() => baseResult.value?.project?.models?.items || [])
|
||||
|
||||
const fetchHubs = async () => {
|
||||
loadingHubs.value = true
|
||||
try {
|
||||
const res = await fetch('https://developer.api.autodesk.com/project/v1/hubs', {
|
||||
headers: { Authorization: `Bearer ${tokens.value!.access_token}` }
|
||||
})
|
||||
|
||||
if (!res.ok) throw new Error('Failed to fetch hubs')
|
||||
hubs.value = (await res.json()).data
|
||||
// console.log('Hubs', hubs.value)
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Failed to fetch hubs',
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
} finally {
|
||||
loadingHubs.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onHubClick = async (hub: AccHub) => {
|
||||
selectedHub.value = hub
|
||||
selectedHubId.value = hub.id
|
||||
await fetchProjects(hub.id)
|
||||
}
|
||||
|
||||
const fetchProjects = async (hubId: string) => {
|
||||
loadingProjects.value = true
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://developer.api.autodesk.com/project/v1/hubs/${hubId}/projects`,
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) throw new Error('Failed to fetch projects')
|
||||
projects.value = (await res.json()).data
|
||||
// console.log('Projects', projects.value)
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Error fetching projects',
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
} finally {
|
||||
loadingProjects.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onProjectClick = async (hubId: string, projectId: string) => {
|
||||
selectedProjectId.value = projectId
|
||||
loadingFiles.value = true
|
||||
folderContents.value = []
|
||||
const rootFolderId = await getProjectRootFolderId(hubId, projectId)
|
||||
if (rootFolderId) {
|
||||
const collectedFiles = await fetchFolderContents(projectId, rootFolderId, [])
|
||||
folderContents.value = collectedFiles
|
||||
// console.log('collectedFiles under root folder',collectedFiles)
|
||||
}
|
||||
loadingFiles.value = false
|
||||
}
|
||||
|
||||
const getProjectRootFolderId = async (hubId: string, projectId: string) => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://developer.api.autodesk.com/project/v1/hubs/${hubId}/projects/${projectId}`,
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) throw (new Error('Failed to get project details'), null)
|
||||
const r = await res.json()
|
||||
// console.log('root folder id', r)
|
||||
folderUrn.value = r.data.relationships?.rootFolder?.data?.id || null
|
||||
return folderUrn.value
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Error getting project root folder ID',
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const fetchFolderContents = async (
|
||||
projectId: string,
|
||||
folderId: string,
|
||||
collectedItems: AccItem[] = []
|
||||
) => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://developer.api.autodesk.com/data/v1/projects/${projectId}/folders/${folderId}/contents`,
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch contents of folder ${folderId}`)
|
||||
}
|
||||
|
||||
const data = (await res.json()).data
|
||||
const folderPromises: Promise<AccItem[]>[] = []
|
||||
const itemPromises: Promise<void>[] = []
|
||||
|
||||
for (const item of data) {
|
||||
if (item.type === 'folders') {
|
||||
folderPromises.push(fetchFolderContents(projectId, item.id, collectedItems))
|
||||
} else if (item.type === 'items') {
|
||||
itemPromises.push(
|
||||
(async () => {
|
||||
const version = await fetchItemLatestVersion(projectId, item.id)
|
||||
if (version) {
|
||||
const storageUrn = version.relationships?.storage?.data?.id || null
|
||||
collectedItems.push({
|
||||
...item,
|
||||
latestVersionId: version.id,
|
||||
storageUrn
|
||||
})
|
||||
} else {
|
||||
collectedItems.push(item)
|
||||
}
|
||||
})()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([...folderPromises, ...itemPromises])
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: `Error fetching folder contents for ${folderId}:`,
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
}
|
||||
return collectedItems
|
||||
}
|
||||
|
||||
const fetchItemLatestVersion = async (projectId: string, itemId: string) => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://developer.api.autodesk.com/data/v1/projects/${projectId}/items/${encodeURIComponent(
|
||||
itemId
|
||||
)}/versions`,
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch versions for item ${itemId}`)
|
||||
}
|
||||
const versions = (await res.json()).data
|
||||
// console.log('versions', versions)
|
||||
if (versions.length > 0) return versions[0]
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: `Error fetching versions for item ${itemId}:`,
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const getSignedDownloadUrl = async (projectId: string, versionId: string) => {
|
||||
const res = await fetch(
|
||||
`https://developer.api.autodesk.com/data/v1/projects/${projectId}/versions/${encodeURIComponent(
|
||||
versionId
|
||||
)}/download`,
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) throw new Error(`Failed to generate ACC download URL`)
|
||||
const { links } = await res.json()
|
||||
return links.self.href
|
||||
}
|
||||
|
||||
const onDownloadClick = async (item: AccItem) => {
|
||||
try {
|
||||
const signedUrl = await getSignedDownloadUrl(
|
||||
selectedProjectId.value as string,
|
||||
item.latestVersionId as string
|
||||
)
|
||||
window.open(signedUrl, '_blank')
|
||||
} catch (e) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Download failed',
|
||||
description: e instanceof Error ? e.message : 'Unexpected error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const onFileSelected = (item: AccItem) => {
|
||||
selectedFolderContent.value = item
|
||||
}
|
||||
|
||||
// const handleModelSelect = (model: ProjectPageLatestItemsModelItemFragment) => {
|
||||
// selectedModel.value = model
|
||||
// }
|
||||
|
||||
const addSync = async () => {
|
||||
const item = {
|
||||
id: 'whatever',
|
||||
accHub: selectedHub.value,
|
||||
accHubId: selectedHubId.value,
|
||||
accHubUrn: folderUrn.value,
|
||||
modelId: selectedModel.value?.id,
|
||||
projectId: props.projectId,
|
||||
projectName: '',
|
||||
modelName: selectedModel.value?.displayName,
|
||||
createdBy: 'cat',
|
||||
accItem: selectedFolderContent.value,
|
||||
status: 'syncing'
|
||||
} as AccSyncItem
|
||||
internalSyncs.value.push(item)
|
||||
await fetch('/acc/sync-item-created', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(item)
|
||||
})
|
||||
showNewSyncDialog.value = false
|
||||
step.value = 0
|
||||
}
|
||||
|
||||
watch(tokens, (newTokens) => {
|
||||
if (newTokens?.access_token) {
|
||||
fetchHubs()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="flex flex-col text-xs space-y-2">
|
||||
<!-- TODO: Get sync items from graphql -->
|
||||
<ProjectPageAccSyncs
|
||||
:project-id="projectId"
|
||||
:is-logged-in="hasTokens"
|
||||
:tokens="tokens"
|
||||
:syncs="[]"
|
||||
></ProjectPageAccSyncs>
|
||||
|
||||
<div v-if="!hasTokens">
|
||||
<CommonLoadingBar v-if="loadingTokens" :loading="true" class="my-2" />
|
||||
<div v-else>
|
||||
<hr class="mb-2" />
|
||||
<FormButton size="sm" @click="authAcc()">Connect to ACC</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- USER INFO -->
|
||||
<div v-if="userInfo" class="flex flex-col space-y-2">
|
||||
<hr class="my-2" />
|
||||
<div class="flex flex-col text ml-1 space-y-2 mb-2">
|
||||
<span>
|
||||
<strong>Name:</strong>
|
||||
{{ userInfo.firstName }} {{ userInfo.lastName }}
|
||||
</span>
|
||||
<span>
|
||||
<strong>Email:</strong>
|
||||
{{ userInfo.emailId }}
|
||||
</span>
|
||||
<span>
|
||||
<strong>User ID:</strong>
|
||||
{{ userInfo.userId }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<FormButton
|
||||
v-if="hasTokens"
|
||||
class="mt-4"
|
||||
color="outline"
|
||||
size="sm"
|
||||
@click="tokens = undefined"
|
||||
>
|
||||
Log out
|
||||
</FormButton>
|
||||
<div>
|
||||
{{ tokens?.access_token }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AccTokens, AccUserInfo } from '~/lib/acc/types'
|
||||
|
||||
const props = defineProps<{ projectId: string }>()
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
|
||||
const tokens = ref<AccTokens>()
|
||||
const hasTokens = computed(() => !!tokens.value?.access_token)
|
||||
const loadingTokens = ref(true)
|
||||
const userInfo = ref<AccUserInfo>()
|
||||
const loadingUser = ref(false)
|
||||
|
||||
// const syncs = ref<AccSyncItem[]>([
|
||||
// {
|
||||
// id: '1',
|
||||
// projectId: '',
|
||||
// modelId: '',
|
||||
// projectName: 'test',
|
||||
// modelName: 'test',
|
||||
// status: 'paused',
|
||||
// createdBy: 'Oguzhan Koral',
|
||||
// accItem: {
|
||||
// id: 'yo',
|
||||
// attributes: {
|
||||
// name: 'whatever.rvt',
|
||||
// displayName: 'whatever.rvt'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ])
|
||||
|
||||
// AUTH + TOKEN FLOW
|
||||
const fetchTokens = async () => {
|
||||
try {
|
||||
const res = await fetch('/auth/acc/status', { credentials: 'include' })
|
||||
if (!res.ok) return
|
||||
tokens.value = await res.json()
|
||||
} finally {
|
||||
loadingTokens.value = false
|
||||
}
|
||||
}
|
||||
fetchTokens()
|
||||
|
||||
const authAcc = async () => {
|
||||
try {
|
||||
const response = await fetch('/auth/acc/login', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ projectId: props.projectId })
|
||||
})
|
||||
if (!response.ok) throw new Error('Failed to initiate ACC login.')
|
||||
const { authorizeUrl } = await response.json()
|
||||
if (!authorizeUrl) throw new Error('No authorize URL returned by server.')
|
||||
window.location.href = authorizeUrl
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Error starting ACC login',
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const scheduleRefresh = (expiresInSeconds: number) => {
|
||||
const refreshTime = (expiresInSeconds - 60) * 1000
|
||||
setTimeout(async () => {
|
||||
loadingTokens.value = true
|
||||
const res = await fetch('/auth/acc/refresh', {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
})
|
||||
if (res.ok) {
|
||||
const refreshed = await res.json()
|
||||
await fetchTokens()
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Success,
|
||||
title: 'ACC tokens refreshed',
|
||||
description: refreshed
|
||||
})
|
||||
scheduleRefresh(refreshed.expires_in)
|
||||
}
|
||||
loadingTokens.value = false
|
||||
}, refreshTime)
|
||||
}
|
||||
|
||||
watch(tokens, (newTokens) => {
|
||||
if (newTokens?.expires_in) scheduleRefresh(newTokens.expires_in)
|
||||
})
|
||||
watch(tokens, (newTokens) => {
|
||||
if (newTokens?.access_token) {
|
||||
fetchUserInfo()
|
||||
}
|
||||
})
|
||||
|
||||
// USER INFO
|
||||
const fetchUserInfo = async () => {
|
||||
loadingUser.value = true
|
||||
try {
|
||||
const res = await fetch(
|
||||
'https://developer.api.autodesk.com/userprofile/v1/users/@me',
|
||||
{ headers: { Authorization: `Bearer ${tokens.value!.access_token}` } }
|
||||
)
|
||||
if (!res.ok) throw new Error('Failed to get user info directly from ACC')
|
||||
userInfo.value = await res.json()
|
||||
} catch (error) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Error fetching user info directly',
|
||||
description: error instanceof Error ? error.message : 'Unexpected error'
|
||||
})
|
||||
} finally {
|
||||
loadingUser.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
export type AccTokens = {
|
||||
access_token: string
|
||||
refresh_token: string
|
||||
token_type: string
|
||||
id_token: string
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
export type AccUserInfo = {
|
||||
userId: string
|
||||
userName: string
|
||||
emailId: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
}
|
||||
|
||||
export type AccHub = {
|
||||
id: string
|
||||
attributes: { name: string; extension: Record<string, unknown> }
|
||||
}
|
||||
|
||||
export type AccProject = {
|
||||
id: string
|
||||
attributes: { name: string; lastModifiedTime: string }
|
||||
relationships: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type AccItem = {
|
||||
id: string
|
||||
type?: string
|
||||
latestVersionId?: string // we mutate on the way
|
||||
storageUrn?: string // we mutate on the way
|
||||
attributes: {
|
||||
name: string
|
||||
displayName: string
|
||||
createTime?: string
|
||||
extension?: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
export type AccSyncItem = {
|
||||
id: string
|
||||
accHub: AccHub
|
||||
accHubId: string
|
||||
createdBy: string
|
||||
projectId: string
|
||||
modelId: string
|
||||
projectName: string
|
||||
modelName: string
|
||||
accItem: AccItem
|
||||
status: AccSyncItemStatus
|
||||
}
|
||||
|
||||
export type AccSyncItemStatus = 'sync' | 'syncing' | 'paused' | 'failed'
|
||||
@@ -934,6 +934,15 @@ export type CreateServerRegionInput = {
|
||||
name: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateSyncItemInput = {
|
||||
accFileLineageId: Scalars['String']['input'];
|
||||
accHubId: Scalars['String']['input'];
|
||||
accProjectId: Scalars['String']['input'];
|
||||
accRootFolderUrn: Scalars['String']['input'];
|
||||
modelId: Scalars['String']['input'];
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateUserEmailInput = {
|
||||
email: Scalars['String']['input'];
|
||||
};
|
||||
@@ -965,6 +974,11 @@ export type DeleteModelInput = {
|
||||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteUserEmailInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
@@ -1607,6 +1621,7 @@ export type Mutation = {
|
||||
streamUpdatePermission?: Maybe<Scalars['Boolean']['output']>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.batchDelete instead. */
|
||||
streamsDelete: Scalars['Boolean']['output'];
|
||||
syncItemMutations: SyncItemMutations;
|
||||
/**
|
||||
* Used for broadcasting real time typing status in comment threads. Does not persist any info.
|
||||
* @deprecated Use broadcastViewerUserActivity
|
||||
@@ -2123,6 +2138,8 @@ export type Project = {
|
||||
role?: Maybe<Scalars['String']['output']>;
|
||||
/** Source apps used in any models of this project */
|
||||
sourceApps: Array<Scalars['String']['output']>;
|
||||
syncItem: SyncItem;
|
||||
syncItems: SyncItemCollection;
|
||||
team: Array<ProjectCollaborator>;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
/** Retrieve a specific project version by its ID */
|
||||
@@ -2230,6 +2247,11 @@ export type ProjectPendingImportedModelsArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type ProjectSyncItemArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectVersionArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3645,6 +3667,7 @@ export type Subscription = {
|
||||
projectPendingModelsUpdated: ProjectPendingModelsUpdatedMessage;
|
||||
/** Subscribe to changes to a project's pending versions */
|
||||
projectPendingVersionsUpdated: ProjectPendingVersionsUpdatedMessage;
|
||||
projectSyncItemsUpdated: Scalars['String']['output'];
|
||||
/** Subscribe to updates to any triggered automations statuses in the project */
|
||||
projectTriggeredAutomationsStatusUpdated: ProjectTriggeredAutomationsStatusUpdatedMessage;
|
||||
/** Track updates to a specific project */
|
||||
@@ -3774,6 +3797,12 @@ export type SubscriptionProjectPendingVersionsUpdatedArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectSyncItemsUpdatedArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
itemIds?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectTriggeredAutomationsStatusUpdatedArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3839,6 +3868,59 @@ export type SubscriptionWorkspaceUpdatedArgs = {
|
||||
workspaceSlug?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type SyncItem = {
|
||||
__typename?: 'SyncItem';
|
||||
accFileLineageId: Scalars['String']['output'];
|
||||
accHubId: Scalars['String']['output'];
|
||||
accProjectId: Scalars['String']['output'];
|
||||
accRootFolderUrn: Scalars['String']['output'];
|
||||
accWebhookId: Scalars['String']['output'];
|
||||
author?: Maybe<LimitedUser>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
modelId: Scalars['String']['output'];
|
||||
projectId: Scalars['String']['output'];
|
||||
status: SyncItemStatus;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type SyncItemCollection = {
|
||||
__typename?: 'SyncItemCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
items: Array<SyncItem>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type SyncItemMutations = {
|
||||
__typename?: 'SyncItemMutations';
|
||||
create: SyncItem;
|
||||
delete: Scalars['Boolean']['output'];
|
||||
update: SyncItem;
|
||||
};
|
||||
|
||||
|
||||
export type SyncItemMutationsCreateArgs = {
|
||||
input: CreateSyncItemInput;
|
||||
};
|
||||
|
||||
|
||||
export type SyncItemMutationsDeleteArgs = {
|
||||
input: DeleteSyncItemInput;
|
||||
};
|
||||
|
||||
|
||||
export type SyncItemMutationsUpdateArgs = {
|
||||
input: UpdateSyncItemInput;
|
||||
};
|
||||
|
||||
export const SyncItemStatus = {
|
||||
Failed: 'FAILED',
|
||||
Paused: 'PAUSED',
|
||||
Sync: 'SYNC',
|
||||
Syncing: 'SYNCING'
|
||||
} as const;
|
||||
|
||||
export type SyncItemStatus = typeof SyncItemStatus[keyof typeof SyncItemStatus];
|
||||
export type TestAutomationRun = {
|
||||
__typename?: 'TestAutomationRun';
|
||||
automationRunId: Scalars['String']['output'];
|
||||
@@ -3908,6 +3990,12 @@ export type UpdateServerRegionInput = {
|
||||
name?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type UpdateSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
status: SyncItemStatus;
|
||||
};
|
||||
|
||||
/** Only non-null values will be updated */
|
||||
export type UpdateVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -7969,6 +8057,9 @@ export type AllObjectTypes = {
|
||||
StreamCollaborator: StreamCollaborator,
|
||||
StreamCollection: StreamCollection,
|
||||
Subscription: Subscription,
|
||||
SyncItem: SyncItem,
|
||||
SyncItemCollection: SyncItemCollection,
|
||||
SyncItemMutations: SyncItemMutations,
|
||||
TestAutomationRun: TestAutomationRun,
|
||||
TestAutomationRunTrigger: TestAutomationRunTrigger,
|
||||
TestAutomationRunTriggerPayload: TestAutomationRunTriggerPayload,
|
||||
@@ -8561,6 +8652,7 @@ export type MutationFieldArgs = {
|
||||
streamUpdate: MutationStreamUpdateArgs,
|
||||
streamUpdatePermission: MutationStreamUpdatePermissionArgs,
|
||||
streamsDelete: MutationStreamsDeleteArgs,
|
||||
syncItemMutations: {},
|
||||
userCommentThreadActivityBroadcast: MutationUserCommentThreadActivityBroadcastArgs,
|
||||
userDelete: MutationUserDeleteArgs,
|
||||
userNotificationPreferencesUpdate: MutationUserNotificationPreferencesUpdateArgs,
|
||||
@@ -8662,6 +8754,8 @@ export type ProjectFieldArgs = {
|
||||
permissions: {},
|
||||
role: {},
|
||||
sourceApps: {},
|
||||
syncItem: ProjectSyncItemArgs,
|
||||
syncItems: {},
|
||||
team: {},
|
||||
updatedAt: {},
|
||||
version: ProjectVersionArgs,
|
||||
@@ -9045,6 +9139,7 @@ export type SubscriptionFieldArgs = {
|
||||
projectModelsUpdated: SubscriptionProjectModelsUpdatedArgs,
|
||||
projectPendingModelsUpdated: SubscriptionProjectPendingModelsUpdatedArgs,
|
||||
projectPendingVersionsUpdated: SubscriptionProjectPendingVersionsUpdatedArgs,
|
||||
projectSyncItemsUpdated: SubscriptionProjectSyncItemsUpdatedArgs,
|
||||
projectTriggeredAutomationsStatusUpdated: SubscriptionProjectTriggeredAutomationsStatusUpdatedArgs,
|
||||
projectUpdated: SubscriptionProjectUpdatedArgs,
|
||||
projectVersionGendoAIRenderCreated: SubscriptionProjectVersionGendoAiRenderCreatedArgs,
|
||||
@@ -9061,6 +9156,30 @@ export type SubscriptionFieldArgs = {
|
||||
workspaceProjectsUpdated: SubscriptionWorkspaceProjectsUpdatedArgs,
|
||||
workspaceUpdated: SubscriptionWorkspaceUpdatedArgs,
|
||||
}
|
||||
export type SyncItemFieldArgs = {
|
||||
accFileLineageId: {},
|
||||
accHubId: {},
|
||||
accProjectId: {},
|
||||
accRootFolderUrn: {},
|
||||
accWebhookId: {},
|
||||
author: {},
|
||||
createdAt: {},
|
||||
id: {},
|
||||
modelId: {},
|
||||
projectId: {},
|
||||
status: {},
|
||||
updatedAt: {},
|
||||
}
|
||||
export type SyncItemCollectionFieldArgs = {
|
||||
cursor: {},
|
||||
items: {},
|
||||
totalCount: {},
|
||||
}
|
||||
export type SyncItemMutationsFieldArgs = {
|
||||
create: SyncItemMutationsCreateArgs,
|
||||
delete: SyncItemMutationsDeleteArgs,
|
||||
update: SyncItemMutationsUpdateArgs,
|
||||
}
|
||||
export type TestAutomationRunFieldArgs = {
|
||||
automationRunId: {},
|
||||
functionRunId: {},
|
||||
@@ -9595,6 +9714,9 @@ export type AllObjectFieldArgTypes = {
|
||||
StreamCollaborator: StreamCollaboratorFieldArgs,
|
||||
StreamCollection: StreamCollectionFieldArgs,
|
||||
Subscription: SubscriptionFieldArgs,
|
||||
SyncItem: SyncItemFieldArgs,
|
||||
SyncItemCollection: SyncItemCollectionFieldArgs,
|
||||
SyncItemMutations: SyncItemMutationsFieldArgs,
|
||||
TestAutomationRun: TestAutomationRunFieldArgs,
|
||||
TestAutomationRunTrigger: TestAutomationRunTriggerFieldArgs,
|
||||
TestAutomationRunTriggerPayload: TestAutomationRunTriggerPayloadFieldArgs,
|
||||
|
||||
@@ -14,6 +14,8 @@ export const forgottenPasswordRoute = '/authn/forgotten-password'
|
||||
export const verifyEmailRoute = '/verify-email'
|
||||
export const verifyEmailCountdownRoute = '/verify-email?source=registration'
|
||||
export const serverManagementRoute = '/server-management'
|
||||
export const accLoginRoute = '/authn/acc'
|
||||
export const accRoute = '/acc'
|
||||
export const connectorsRoute = '/connectors'
|
||||
export const tutorialsRoute = '/tutorials'
|
||||
export const docsPageUrl = 'https://docs.speckle.systems/'
|
||||
@@ -79,7 +81,7 @@ export const settingsWorkspaceRoutes = {
|
||||
|
||||
export const projectRoute = (
|
||||
id: string,
|
||||
tab?: 'models' | 'discussions' | 'automations' | 'collaborators' | 'settings'
|
||||
tab?: 'models' | 'discussions' | 'automations' | 'collaborators' | 'settings' | 'acc'
|
||||
) => {
|
||||
let res = `/projects/${id}`
|
||||
if (tab && tab !== 'models') {
|
||||
|
||||
@@ -255,6 +255,14 @@ const pageTabItems = computed((): LayoutPageTabItem[] => {
|
||||
})
|
||||
}
|
||||
|
||||
if (isAccEnabled.value) {
|
||||
//and the rest of checks
|
||||
items.push({
|
||||
title: 'ACC',
|
||||
id: 'acc'
|
||||
})
|
||||
}
|
||||
|
||||
if (canReadSettings.value?.authorized) {
|
||||
items.push({
|
||||
title: 'Collaborators',
|
||||
@@ -270,6 +278,8 @@ const pageTabItems = computed((): LayoutPageTabItem[] => {
|
||||
return items
|
||||
})
|
||||
|
||||
const isAccEnabled = ref(true) // TODO
|
||||
|
||||
const findTabById = (id: string) =>
|
||||
pageTabItems.value.find((tab) => tab.id === id) || pageTabItems.value[0]
|
||||
|
||||
@@ -286,6 +296,7 @@ const activePageTab = computed({
|
||||
const path = router.currentRoute.value.path
|
||||
if (/\/discussions\/?$/i.test(path)) return findTabById('discussions')
|
||||
if (/\/automations\/?.*$/i.test(path)) return findTabById('automations')
|
||||
if (/\/acc\/?.*$/i.test(path)) return findTabById('acc')
|
||||
if (/\/collaborators\/?/i.test(path) && canReadSettings.value?.authorized)
|
||||
return findTabById('collaborators')
|
||||
if (/\/settings\/?/i.test(path) && canReadSettings.value?.authorized)
|
||||
@@ -301,6 +312,9 @@ const activePageTab = computed({
|
||||
case 'discussions':
|
||||
router.push({ path: projectRoute(projectId.value, 'discussions') })
|
||||
break
|
||||
case 'acc':
|
||||
router.push({ path: projectRoute(projectId.value, 'acc') })
|
||||
break
|
||||
case 'automations':
|
||||
router.push({ path: projectRoute(projectId.value, 'automations') })
|
||||
break
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<ProjectPageAccTab :project-id="projectId" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { ProjectPageProjectFragment } from '~~/lib/common/generated/gql/graphql'
|
||||
const route = useRoute()
|
||||
|
||||
const attrs = useAttrs() as {
|
||||
project: ProjectPageProjectFragment
|
||||
}
|
||||
|
||||
const projectName = computed(() =>
|
||||
attrs.project.name.length ? attrs.project.name : ''
|
||||
)
|
||||
|
||||
const projectId = computed(() => route.params.id as string)
|
||||
|
||||
useHead({
|
||||
title: `Acc | ${projectName.value}`
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
extend type Project {
|
||||
accSyncItems: AccSyncItemCollection!
|
||||
accSyncItem(id: String!): AccSyncItem!
|
||||
}
|
||||
|
||||
type AccSyncItemCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [AccSyncItem!]!
|
||||
}
|
||||
|
||||
type AccSyncItem {
|
||||
id: ID!
|
||||
projectId: String!
|
||||
modelId: String!
|
||||
accHubId: String!
|
||||
accProjectId: String!
|
||||
accRootFolderUrn: String!
|
||||
accFileLineageId: String!
|
||||
accWebhookId: String
|
||||
status: AccSyncItemStatus!
|
||||
author: LimitedUser
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
}
|
||||
|
||||
enum AccSyncItemStatus {
|
||||
SYNC
|
||||
SYNCING
|
||||
FAILED
|
||||
PAUSED
|
||||
}
|
||||
|
||||
input DeleteAccSyncItemInput {
|
||||
projectId: ID!
|
||||
accFileLineageId: ID!
|
||||
}
|
||||
|
||||
input UpdateAccSyncItemInput {
|
||||
projectId: ID!
|
||||
accFileLineageId: ID!
|
||||
status: AccSyncItemStatus!
|
||||
}
|
||||
|
||||
input CreateAccSyncItemInput {
|
||||
projectId: String!
|
||||
modelId: String!
|
||||
accHubId: String!
|
||||
accProjectId: String!
|
||||
accRootFolderUrn: String!
|
||||
accFileLineageId: String!
|
||||
}
|
||||
|
||||
type AccSyncItemMutations {
|
||||
create(input: CreateAccSyncItemInput!): AccSyncItem
|
||||
# delete(input: DeleteAccSyncItemInput!): Boolean!
|
||||
# update(input: UpdateAccSyncItemInput!): AccSyncItem!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
accSyncItemMutations: AccSyncItemMutations!
|
||||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "streams:write")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
projectAccSyncItemsUpdated(id: String!, itemIds: [String!]): String!
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { AccSyncItem } from '@/modules/acc/helpers/types'
|
||||
import {
|
||||
DeleteAccSyncItemInput,
|
||||
UpdateAccSyncItemInput
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
export const accSyncItemEventsNamespace = 'accSyncItems' as const
|
||||
|
||||
export const AccSyncItemEvents = {
|
||||
Created: `${accSyncItemEventsNamespace}:created`,
|
||||
Updated: `${accSyncItemEventsNamespace}:updated`,
|
||||
Deleted: `${accSyncItemEventsNamespace}:deleted`
|
||||
} as const
|
||||
|
||||
export type AccSyncItemEventsPayloads = {
|
||||
[AccSyncItemEvents.Created]: {
|
||||
syncItem: AccSyncItem
|
||||
projectId: string
|
||||
}
|
||||
|
||||
[AccSyncItemEvents.Updated]: {
|
||||
oldSyncItem: AccSyncItem
|
||||
newSyncItem: AccSyncItem
|
||||
projectId: string
|
||||
userId?: string
|
||||
input: UpdateAccSyncItemInput
|
||||
}
|
||||
|
||||
[AccSyncItemEvents.Deleted]: {
|
||||
syncItem: AccSyncItem
|
||||
projectId: string
|
||||
userId?: string
|
||||
input: DeleteAccSyncItemInput
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { AccSyncItem } from '@/modules/acc/helpers/types'
|
||||
import { createAccSyncItemAndNotifyFactory } from '@/modules/acc/repositories/accSyncItems'
|
||||
import { TokenResourceIdentifierType } from '@/modules/core/domain/tokens/types'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
import { throwIfResourceAccessNotAllowed } from '@/modules/core/helpers/token'
|
||||
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import { GraphQLError } from 'graphql/error'
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const ACC_SYNC_ITEMS = 'acc_sync_items'
|
||||
|
||||
const tables = {
|
||||
accSyncItems: (db: Knex) => db<AccSyncItem>(ACC_SYNC_ITEMS)
|
||||
}
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
Project: {
|
||||
async accSyncItems(parent, args, ctx) {
|
||||
throwIfResourceAccessNotAllowed({
|
||||
resourceId: parent.id,
|
||||
resourceAccessRules: ctx.resourceAccessRules,
|
||||
resourceType: TokenResourceIdentifierType.Project
|
||||
})
|
||||
|
||||
const projectDB = await getProjectDbClient({ projectId: parent.id })
|
||||
|
||||
const items = await tables
|
||||
.accSyncItems(projectDB)
|
||||
.where({ projectId: parent.id })
|
||||
.orderBy('createdAt', 'desc')
|
||||
|
||||
return {
|
||||
totalCount: items.length,
|
||||
cursor: null, // TODO
|
||||
items: items.map((item) => ({
|
||||
...item,
|
||||
author: null // TODO
|
||||
}))
|
||||
}
|
||||
},
|
||||
async accSyncItem(parent, args, ctx) {
|
||||
const { id } = args
|
||||
throwIfResourceAccessNotAllowed({
|
||||
resourceId: parent.id,
|
||||
resourceAccessRules: ctx.resourceAccessRules,
|
||||
resourceType: TokenResourceIdentifierType.Project
|
||||
})
|
||||
|
||||
// Get project-scoped DB
|
||||
const projectDB = await getProjectDbClient({ projectId: parent.id })
|
||||
const item = await tables.accSyncItems(projectDB).where({ id }).first()
|
||||
|
||||
if (!item) throw new Error(`SyncItem with id "${id}" not found`) // TODO: create acc kind error types later
|
||||
|
||||
return {
|
||||
...item,
|
||||
author: null // TODO
|
||||
}
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
accSyncItemMutations: () => ({})
|
||||
},
|
||||
AccSyncItemMutations: {
|
||||
async create(parent, args, ctx) {
|
||||
const { input } = args
|
||||
console.log('create', input)
|
||||
throwIfResourceAccessNotAllowed({
|
||||
resourceId: input.projectId,
|
||||
resourceAccessRules: ctx.resourceAccessRules,
|
||||
resourceType: TokenResourceIdentifierType.Project
|
||||
})
|
||||
|
||||
const projectDB = await getProjectDbClient({ projectId: input.projectId })
|
||||
|
||||
const existing = await tables
|
||||
.accSyncItems(projectDB)
|
||||
.where({ accFileLineageId: input.accFileLineageId })
|
||||
.first()
|
||||
|
||||
if (existing) {
|
||||
throw new GraphQLError(
|
||||
`A SyncItem with accFileLineageId "${input.accFileLineageId}" already exists.`,
|
||||
{
|
||||
extensions: { code: 'DUPLICATE_ACC_FILE_LINEAGE_ID' }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const createSyncItem = createAccSyncItemAndNotifyFactory({
|
||||
db: await getProjectDbClient({ projectId: input.projectId }),
|
||||
eventEmit: getEventBus().emit
|
||||
})
|
||||
|
||||
const newItem = await createSyncItem({
|
||||
id: cryptoRandomString({ length: 10 }),
|
||||
status: 'SYNCING',
|
||||
...input
|
||||
})
|
||||
|
||||
return newItem
|
||||
}
|
||||
// async update(parent, args, ctx) {
|
||||
// console.log('update', args)
|
||||
// },
|
||||
// async delete(parent, args, ctx) {
|
||||
// console.log('delete', args)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
export default resolvers
|
||||
@@ -0,0 +1,46 @@
|
||||
import { UserRecord } from '@/modules/core/helpers/types'
|
||||
import type { Session, SessionData } from 'express-session'
|
||||
|
||||
declare module 'express-session' {
|
||||
interface SessionData extends AccSessionData {}
|
||||
}
|
||||
|
||||
declare module 'http' {
|
||||
interface IncomingMessage extends AccSessionData {
|
||||
/**
|
||||
* Not sure why I have to do this, the session type is picked up correctly in some places, but not others
|
||||
*/
|
||||
session: Session & Partial<SessionData>
|
||||
}
|
||||
}
|
||||
|
||||
type AccTokens = {
|
||||
access_token: string
|
||||
refresh_token: string
|
||||
token_type: string
|
||||
id_token: string
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
export type AccSessionData = {
|
||||
accTokens?: AccTokens
|
||||
codeVerifier?: string
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
export type AccSyncItem = {
|
||||
id: string
|
||||
projectId: string
|
||||
modelId: string
|
||||
accHubId: string
|
||||
accProjectId: string
|
||||
accRootFolderUrn: string
|
||||
accFileLineageId: string
|
||||
accWebhookId?: string
|
||||
status: AccSyncItemStatus
|
||||
author: UserRecord
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export type AccSyncItemStatus = 'SYNC' | 'SYNCING' | 'PAUSED' | 'FAILED'
|
||||
@@ -0,0 +1,138 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { createAccOidcFlow } from '@/modules/acc/oidcHelper'
|
||||
import { registerAccWebhook } from '@/modules/acc/webhook'
|
||||
import { sessionMiddlewareFactory } from '@/modules/auth/middleware'
|
||||
import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
||||
import { moduleLogger } from '@/observability/logging'
|
||||
import { Express } from 'express'
|
||||
|
||||
export default function accRestApi(app: Express) {
|
||||
const sessionMiddleware = sessionMiddlewareFactory()
|
||||
app.post('/auth/acc/login', sessionMiddleware, async (req, res) => {
|
||||
const { projectId } = req.body
|
||||
req.session.projectId = projectId
|
||||
|
||||
const accFlow = createAccOidcFlow()
|
||||
const { codeVerifier, codeChallenge } = accFlow.generateCodeVerifier()
|
||||
req.session.codeVerifier = codeVerifier
|
||||
|
||||
const authorizeUrl = accFlow.buildAuthorizeUrl({
|
||||
clientId: process.env.ACC_CLIENT_ID ?? '',
|
||||
redirectUri: process.env.ACC_REDIRECT_URL ?? '',
|
||||
codeChallenge,
|
||||
scopes: [
|
||||
'user-profile:read',
|
||||
'data:read',
|
||||
'data:create',
|
||||
'viewables:read',
|
||||
'openid'
|
||||
]
|
||||
})
|
||||
|
||||
return res.json({ authorizeUrl })
|
||||
})
|
||||
|
||||
app.get('/auth/acc/callback', sessionMiddleware, async (req, res) => {
|
||||
const { code } = req.query
|
||||
const codeVerifier = req.session.codeVerifier
|
||||
|
||||
if (!code || !codeVerifier) {
|
||||
return res.status(400).send({ error: 'Missing code or verifier' })
|
||||
}
|
||||
|
||||
const accFlow = createAccOidcFlow()
|
||||
try {
|
||||
const tokens = await accFlow.exchangeCodeForTokens({
|
||||
code: String(code),
|
||||
codeVerifier,
|
||||
clientId: process.env.ACC_CLIENT_ID ?? '',
|
||||
clientSecret: process.env.ACC_CLIENT_SECRET ?? '',
|
||||
redirectUri: process.env.ACC_REDIRECT_URL ?? ''
|
||||
})
|
||||
|
||||
req.session.accTokens = tokens
|
||||
|
||||
return res.redirect(`/projects/${req.session.projectId}/acc`)
|
||||
} catch (error) {
|
||||
console.error('Token exchange failed:', error)
|
||||
return res.status(500).send({ error: 'Token exchange failed' })
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/auth/acc/status', sessionMiddleware, (req, res) => {
|
||||
if (!req.session.accTokens) {
|
||||
return res.status(404).send({ error: 'No ACC tokens found' })
|
||||
}
|
||||
res.send(req.session.accTokens)
|
||||
})
|
||||
|
||||
app.post('/auth/acc/refresh', sessionMiddleware, async (req, res) => {
|
||||
const { refresh_token } = req.session.accTokens || {}
|
||||
if (!refresh_token) {
|
||||
return res.status(401).json({ error: 'No refresh token found' })
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'refresh_token',
|
||||
client_id: process.env.ACC_CLIENT_ID ?? '',
|
||||
client_secret: process.env.ACC_CLIENT_SECRET ?? '',
|
||||
refresh_token
|
||||
})
|
||||
|
||||
const response = await fetch(
|
||||
'https://developer.api.autodesk.com/authentication/v2/token',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: params
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(await response.text())
|
||||
return res.status(500).json({ error: 'Failed to refresh token' })
|
||||
}
|
||||
|
||||
const newTokens = await response.json()
|
||||
req.session.accTokens = newTokens
|
||||
|
||||
res.json(newTokens)
|
||||
} catch (error) {
|
||||
console.error('Error refreshing token:', error)
|
||||
res.status(500).json({ error: 'Error refreshing token' })
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/acc/sync-item-created', sessionMiddleware, async (req, res) => {
|
||||
const { accHubUrn } = req.body
|
||||
console.log(req.body)
|
||||
console.log(accHubUrn)
|
||||
|
||||
if (!req.session.accTokens) {
|
||||
throw new Error('whatever')
|
||||
}
|
||||
const { access_token } = req.session.accTokens
|
||||
await registerAccWebhook({
|
||||
accessToken: access_token,
|
||||
hubUrn: accHubUrn,
|
||||
region: 'EMEA',
|
||||
event: ''
|
||||
})
|
||||
res.status(200)
|
||||
})
|
||||
|
||||
app.post('/acc/webhook/callback', sessionMiddleware, async (req, res) => {
|
||||
console.log(req.body)
|
||||
res.status(200)
|
||||
})
|
||||
}
|
||||
|
||||
export const init: SpeckleModule['init'] = async ({ app }) => {
|
||||
moduleLogger.info('🔑 Init acc module')
|
||||
|
||||
// Hoist rest
|
||||
accRestApi(app)
|
||||
}
|
||||
|
||||
export const finalize: SpeckleModule['finalize'] = async () => {}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const TABLE_NAME = 'acc_sync_items'
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.createTable(TABLE_NAME, (table) => {
|
||||
table.string('id', 10).primary()
|
||||
table.string('projectId').notNullable().references('id').inTable('streams')
|
||||
table.string('modelId').notNullable()
|
||||
table.string('accHubId').notNullable()
|
||||
table.string('accProjectId').notNullable()
|
||||
table.string('accRootFolderUrn').notNullable()
|
||||
table.string('accFileLineageId').notNullable().unique()
|
||||
table.string('accWebhookId').nullable()
|
||||
table
|
||||
.enu('status', ['SYNC', 'SYNCING', 'FAILED', 'PAUSED'])
|
||||
.notNullable()
|
||||
.defaultTo('SYNC')
|
||||
|
||||
// Foreign key to users table if needed
|
||||
table.string('authorId').nullable()
|
||||
|
||||
table
|
||||
.timestamp('createdAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
table
|
||||
.timestamp('updatedAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
})
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTable(TABLE_NAME)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* eslint-disable camelcase */
|
||||
// modules/accIntegration/oidcHelper.ts
|
||||
import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
|
||||
interface BuildAuthorizeUrlOptions {
|
||||
clientId: string
|
||||
redirectUri: string
|
||||
codeChallenge: string
|
||||
scopes: string[]
|
||||
}
|
||||
|
||||
interface ExchangeCodeOptions {
|
||||
code: string
|
||||
codeVerifier: string
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
redirectUri: string
|
||||
}
|
||||
|
||||
export function createAccOidcFlow() {
|
||||
return {
|
||||
generateCodeVerifier() {
|
||||
const codeVerifier = crypto.randomBytes(32).toString('base64url')
|
||||
const codeChallenge = crypto
|
||||
.createHash('sha256')
|
||||
.update(codeVerifier)
|
||||
.digest('base64url')
|
||||
return { codeVerifier, codeChallenge }
|
||||
},
|
||||
|
||||
buildAuthorizeUrl({
|
||||
clientId,
|
||||
redirectUri,
|
||||
codeChallenge,
|
||||
scopes
|
||||
}: BuildAuthorizeUrlOptions) {
|
||||
const params = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: clientId,
|
||||
redirect_uri: redirectUri,
|
||||
scope: scopes.join(' '),
|
||||
code_challenge: codeChallenge,
|
||||
code_challenge_method: 'S256'
|
||||
})
|
||||
|
||||
return `https://developer.api.autodesk.com/authentication/v2/authorize?${params.toString()}`
|
||||
},
|
||||
|
||||
async exchangeCodeForTokens({
|
||||
code,
|
||||
codeVerifier,
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectUri
|
||||
}: ExchangeCodeOptions) {
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
code,
|
||||
code_verifier: codeVerifier
|
||||
})
|
||||
|
||||
const response = await axios.post(
|
||||
'https://developer.api.autodesk.com/authentication/v2/token',
|
||||
params.toString(),
|
||||
{
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
}
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return response.data // includes access_token, refresh_token, expires_in, token_type, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { AccSyncItemEvents } from '@/modules/acc/domain/events'
|
||||
import { AccSyncItem } from '@/modules/acc/helpers/types'
|
||||
import { EventBusEmit } from '@/modules/shared/services/eventBus'
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const ACC_SYNC_ITEMS = 'acc_sync_items'
|
||||
|
||||
const tables = {
|
||||
accSyncItems: (db: Knex) => db<AccSyncItem>(ACC_SYNC_ITEMS)
|
||||
}
|
||||
|
||||
export type CreateAccSyncItemAndNotify = (
|
||||
input: Omit<AccSyncItem, 'author' | 'createdAt' | 'updatedAt'>
|
||||
) => Promise<AccSyncItem>
|
||||
|
||||
export const createAccSyncItemAndNotifyFactory = (deps: {
|
||||
db: Knex
|
||||
eventEmit: EventBusEmit
|
||||
}): CreateAccSyncItemAndNotify => {
|
||||
return async (input) => {
|
||||
const now = new Date()
|
||||
|
||||
const [item] = await tables
|
||||
.accSyncItems(deps.db)
|
||||
.insert({
|
||||
...input,
|
||||
createdAt: now,
|
||||
updatedAt: now
|
||||
})
|
||||
.returning('*')
|
||||
|
||||
await deps.eventEmit({
|
||||
eventName: AccSyncItemEvents.Created,
|
||||
payload: {
|
||||
syncItem: item,
|
||||
projectId: item.projectId
|
||||
}
|
||||
})
|
||||
|
||||
return item
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
const accWebhookCallbackUrl =
|
||||
'https://oguzhans-macbook-pro.mermaid-emperor.ts.net//acc/webhook/callback'
|
||||
|
||||
export async function registerAccWebhook({
|
||||
accessToken,
|
||||
hubUrn,
|
||||
region,
|
||||
event = 'dm.lineage.updated'
|
||||
}: {
|
||||
accessToken: string
|
||||
hubUrn: string
|
||||
region: string
|
||||
event: string
|
||||
}) {
|
||||
const response = await fetch(
|
||||
`https://developer.api.autodesk.com/webhooks/v1/systems/data/events/${event}/hooks`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'x-ads-region': `${region}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
callbackUrl: accWebhookCallbackUrl,
|
||||
scope: {
|
||||
folder: {
|
||||
hubUrn
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Webhook registration failed: ${await response.text()}`)
|
||||
}
|
||||
|
||||
const res = await response.json()
|
||||
console.log(res)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return res
|
||||
}
|
||||
@@ -40,6 +40,47 @@ export type Scalars = {
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
||||
export type AccSyncItem = {
|
||||
__typename?: 'AccSyncItem';
|
||||
accFileLineageId: Scalars['String']['output'];
|
||||
accHubId: Scalars['String']['output'];
|
||||
accProjectId: Scalars['String']['output'];
|
||||
accRootFolderUrn: Scalars['String']['output'];
|
||||
accWebhookId?: Maybe<Scalars['String']['output']>;
|
||||
author?: Maybe<LimitedUser>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
modelId: Scalars['String']['output'];
|
||||
projectId: Scalars['String']['output'];
|
||||
status: AccSyncItemStatus;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemCollection = {
|
||||
__typename?: 'AccSyncItemCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
items: Array<AccSyncItem>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemMutations = {
|
||||
__typename?: 'AccSyncItemMutations';
|
||||
create?: Maybe<AccSyncItem>;
|
||||
};
|
||||
|
||||
|
||||
export type AccSyncItemMutationsCreateArgs = {
|
||||
input: CreateAccSyncItemInput;
|
||||
};
|
||||
|
||||
export const AccSyncItemStatus = {
|
||||
Failed: 'FAILED',
|
||||
Paused: 'PAUSED',
|
||||
Sync: 'SYNC',
|
||||
Syncing: 'SYNCING'
|
||||
} as const;
|
||||
|
||||
export type AccSyncItemStatus = typeof AccSyncItemStatus[keyof typeof AccSyncItemStatus];
|
||||
export type ActiveUserMutations = {
|
||||
__typename?: 'ActiveUserMutations';
|
||||
emailMutations: UserEmailMutations;
|
||||
@@ -908,6 +949,15 @@ export type CountOnlyCollection = {
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type CreateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['String']['input'];
|
||||
accHubId: Scalars['String']['input'];
|
||||
accProjectId: Scalars['String']['input'];
|
||||
accRootFolderUrn: Scalars['String']['input'];
|
||||
modelId: Scalars['String']['input'];
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateAutomateFunctionInput = {
|
||||
description: Scalars['String']['input'];
|
||||
/** Base64 encoded image data string */
|
||||
@@ -983,6 +1033,11 @@ export type CurrencyBasedPrices = {
|
||||
usd: WorkspacePaidPlanPrices;
|
||||
};
|
||||
|
||||
export type DeleteAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
@@ -1468,6 +1523,7 @@ export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
/** The void stares back. */
|
||||
_?: Maybe<Scalars['String']['output']>;
|
||||
accSyncItemMutations: AccSyncItemMutations;
|
||||
/** Various Active User oriented mutations */
|
||||
activeUserMutations: ActiveUserMutations;
|
||||
admin: AdminMutations;
|
||||
@@ -2100,6 +2156,8 @@ export type Price = {
|
||||
|
||||
export type Project = {
|
||||
__typename?: 'Project';
|
||||
accSyncItem: AccSyncItem;
|
||||
accSyncItems: AccSyncItemCollection;
|
||||
allowPublicComments: Scalars['Boolean']['output'];
|
||||
/** Get a single automation by id. Error will be thrown if automation is not found or inaccessible. */
|
||||
automation: Automation;
|
||||
@@ -2161,6 +2219,11 @@ export type Project = {
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccSyncItemArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAutomationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3650,6 +3713,7 @@ export type Subscription = {
|
||||
* Note: Only works in test environment
|
||||
*/
|
||||
ping: Scalars['String']['output'];
|
||||
projectAccSyncItemsUpdated: Scalars['String']['output'];
|
||||
/** Subscribe to updates to automations in the project */
|
||||
projectAutomationsUpdated: ProjectAutomationsUpdatedMessage;
|
||||
/**
|
||||
@@ -3766,6 +3830,12 @@ export type SubscriptionCommitUpdatedArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAccSyncItemsUpdatedArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
itemIds?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAutomationsUpdatedArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3906,6 +3976,12 @@ export type TriggeredAutomationsStatus = {
|
||||
statusMessage?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UpdateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
status: AccSyncItemStatus;
|
||||
};
|
||||
|
||||
/** Any null values will be ignored */
|
||||
export type UpdateAutomateFunctionInput = {
|
||||
description?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -5338,6 +5414,10 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
|
||||
|
||||
/** Mapping between all available schema types and the resolvers types */
|
||||
export type ResolversTypes = {
|
||||
AccSyncItem: ResolverTypeWrapper<Omit<AccSyncItem, 'author'> & { author?: Maybe<ResolversTypes['LimitedUser']> }>;
|
||||
AccSyncItemCollection: ResolverTypeWrapper<Omit<AccSyncItemCollection, 'items'> & { items: Array<ResolversTypes['AccSyncItem']> }>;
|
||||
AccSyncItemMutations: ResolverTypeWrapper<Omit<AccSyncItemMutations, 'create'> & { create?: Maybe<ResolversTypes['AccSyncItem']> }>;
|
||||
AccSyncItemStatus: AccSyncItemStatus;
|
||||
ActiveUserMutations: ResolverTypeWrapper<MutationsObjectGraphQLReturn>;
|
||||
Activity: ResolverTypeWrapper<Activity>;
|
||||
ActivityCollection: ResolverTypeWrapper<ActivityCollectionGraphQLReturn>;
|
||||
@@ -5422,6 +5502,7 @@ export type ResolversTypes = {
|
||||
CommitsDeleteInput: CommitsDeleteInput;
|
||||
CommitsMoveInput: CommitsMoveInput;
|
||||
CountOnlyCollection: ResolverTypeWrapper<CountOnlyCollection>;
|
||||
CreateAccSyncItemInput: CreateAccSyncItemInput;
|
||||
CreateAutomateFunctionInput: CreateAutomateFunctionInput;
|
||||
CreateAutomateFunctionWithoutVersionInput: CreateAutomateFunctionWithoutVersionInput;
|
||||
CreateCommentInput: CreateCommentInput;
|
||||
@@ -5433,6 +5514,7 @@ export type ResolversTypes = {
|
||||
Currency: Currency;
|
||||
CurrencyBasedPrices: ResolverTypeWrapper<Omit<CurrencyBasedPrices, 'gbp' | 'usd'> & { gbp: ResolversTypes['WorkspacePaidPlanPrices'], usd: ResolversTypes['WorkspacePaidPlanPrices'] }>;
|
||||
DateTime: ResolverTypeWrapper<Scalars['DateTime']['output']>;
|
||||
DeleteAccSyncItemInput: DeleteAccSyncItemInput;
|
||||
DeleteModelInput: DeleteModelInput;
|
||||
DeleteUserEmailInput: DeleteUserEmailInput;
|
||||
DeleteVersionsInput: DeleteVersionsInput;
|
||||
@@ -5581,6 +5663,7 @@ export type ResolversTypes = {
|
||||
TokenResourceIdentifierInput: TokenResourceIdentifierInput;
|
||||
TokenResourceIdentifierType: TokenResourceIdentifierType;
|
||||
TriggeredAutomationsStatus: ResolverTypeWrapper<TriggeredAutomationsStatusGraphQLReturn>;
|
||||
UpdateAccSyncItemInput: UpdateAccSyncItemInput;
|
||||
UpdateAutomateFunctionInput: UpdateAutomateFunctionInput;
|
||||
UpdateModelInput: UpdateModelInput;
|
||||
UpdateServerRegionInput: UpdateServerRegionInput;
|
||||
@@ -5686,6 +5769,9 @@ export type ResolversTypes = {
|
||||
|
||||
/** Mapping between all available schema types and the resolvers parents */
|
||||
export type ResolversParentTypes = {
|
||||
AccSyncItem: Omit<AccSyncItem, 'author'> & { author?: Maybe<ResolversParentTypes['LimitedUser']> };
|
||||
AccSyncItemCollection: Omit<AccSyncItemCollection, 'items'> & { items: Array<ResolversParentTypes['AccSyncItem']> };
|
||||
AccSyncItemMutations: Omit<AccSyncItemMutations, 'create'> & { create?: Maybe<ResolversParentTypes['AccSyncItem']> };
|
||||
ActiveUserMutations: MutationsObjectGraphQLReturn;
|
||||
Activity: Activity;
|
||||
ActivityCollection: ActivityCollectionGraphQLReturn;
|
||||
@@ -5766,6 +5852,7 @@ export type ResolversParentTypes = {
|
||||
CommitsDeleteInput: CommitsDeleteInput;
|
||||
CommitsMoveInput: CommitsMoveInput;
|
||||
CountOnlyCollection: CountOnlyCollection;
|
||||
CreateAccSyncItemInput: CreateAccSyncItemInput;
|
||||
CreateAutomateFunctionInput: CreateAutomateFunctionInput;
|
||||
CreateAutomateFunctionWithoutVersionInput: CreateAutomateFunctionWithoutVersionInput;
|
||||
CreateCommentInput: CreateCommentInput;
|
||||
@@ -5776,6 +5863,7 @@ export type ResolversParentTypes = {
|
||||
CreateVersionInput: CreateVersionInput;
|
||||
CurrencyBasedPrices: Omit<CurrencyBasedPrices, 'gbp' | 'usd'> & { gbp: ResolversParentTypes['WorkspacePaidPlanPrices'], usd: ResolversParentTypes['WorkspacePaidPlanPrices'] };
|
||||
DateTime: Scalars['DateTime']['output'];
|
||||
DeleteAccSyncItemInput: DeleteAccSyncItemInput;
|
||||
DeleteModelInput: DeleteModelInput;
|
||||
DeleteUserEmailInput: DeleteUserEmailInput;
|
||||
DeleteVersionsInput: DeleteVersionsInput;
|
||||
@@ -5906,6 +5994,7 @@ export type ResolversParentTypes = {
|
||||
TokenResourceIdentifier: TokenResourceIdentifier;
|
||||
TokenResourceIdentifierInput: TokenResourceIdentifierInput;
|
||||
TriggeredAutomationsStatus: TriggeredAutomationsStatusGraphQLReturn;
|
||||
UpdateAccSyncItemInput: UpdateAccSyncItemInput;
|
||||
UpdateAutomateFunctionInput: UpdateAutomateFunctionInput;
|
||||
UpdateModelInput: UpdateModelInput;
|
||||
UpdateServerRegionInput: UpdateServerRegionInput;
|
||||
@@ -6033,6 +6122,34 @@ export type IsOwnerDirectiveArgs = { };
|
||||
|
||||
export type IsOwnerDirectiveResolver<Result, Parent, ContextType = GraphQLContext, Args = IsOwnerDirectiveArgs> = DirectiveResolverFn<Result, Parent, ContextType, Args>;
|
||||
|
||||
export type AccSyncItemResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AccSyncItem'] = ResolversParentTypes['AccSyncItem']> = {
|
||||
accFileLineageId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
accHubId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
accProjectId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
accRootFolderUrn?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
accWebhookId?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
author?: Resolver<Maybe<ResolversTypes['LimitedUser']>, ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
modelId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
projectId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
status?: Resolver<ResolversTypes['AccSyncItemStatus'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type AccSyncItemCollectionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AccSyncItemCollection'] = ResolversParentTypes['AccSyncItemCollection']> = {
|
||||
cursor?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
items?: Resolver<Array<ResolversTypes['AccSyncItem']>, ParentType, ContextType>;
|
||||
totalCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type AccSyncItemMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AccSyncItemMutations'] = ResolversParentTypes['AccSyncItemMutations']> = {
|
||||
create?: Resolver<Maybe<ResolversTypes['AccSyncItem']>, ParentType, ContextType, RequireFields<AccSyncItemMutationsCreateArgs, 'input'>>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ActiveUserMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ActiveUserMutations'] = ResolversParentTypes['ActiveUserMutations']> = {
|
||||
emailMutations?: Resolver<ResolversTypes['UserEmailMutations'], ParentType, ContextType>;
|
||||
finishOnboarding?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, Partial<ActiveUserMutationsFinishOnboardingArgs>>;
|
||||
@@ -6660,6 +6777,7 @@ export type ModelsTreeItemCollectionResolvers<ContextType = GraphQLContext, Pare
|
||||
|
||||
export type MutationResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = {
|
||||
_?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
accSyncItemMutations?: Resolver<ResolversTypes['AccSyncItemMutations'], ParentType, ContextType>;
|
||||
activeUserMutations?: Resolver<ResolversTypes['ActiveUserMutations'], ParentType, ContextType>;
|
||||
admin?: Resolver<ResolversTypes['AdminMutations'], ParentType, ContextType>;
|
||||
adminDeleteUser?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationAdminDeleteUserArgs, 'userConfirmation'>>;
|
||||
@@ -6805,6 +6923,8 @@ export type PriceResolvers<ContextType = GraphQLContext, ParentType extends Reso
|
||||
};
|
||||
|
||||
export type ProjectResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['Project'] = ResolversParentTypes['Project']> = {
|
||||
accSyncItem?: Resolver<ResolversTypes['AccSyncItem'], ParentType, ContextType, RequireFields<ProjectAccSyncItemArgs, 'id'>>;
|
||||
accSyncItems?: Resolver<ResolversTypes['AccSyncItemCollection'], ParentType, ContextType>;
|
||||
allowPublicComments?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
automation?: Resolver<ResolversTypes['Automation'], ParentType, ContextType, RequireFields<ProjectAutomationArgs, 'id'>>;
|
||||
automations?: Resolver<ResolversTypes['AutomationCollection'], ParentType, ContextType, Partial<ProjectAutomationsArgs>>;
|
||||
@@ -7304,6 +7424,7 @@ export type SubscriptionResolvers<ContextType = GraphQLContext, ParentType exten
|
||||
commitDeleted?: SubscriptionResolver<Maybe<ResolversTypes['JSONObject']>, "commitDeleted", ParentType, ContextType, RequireFields<SubscriptionCommitDeletedArgs, 'streamId'>>;
|
||||
commitUpdated?: SubscriptionResolver<Maybe<ResolversTypes['JSONObject']>, "commitUpdated", ParentType, ContextType, RequireFields<SubscriptionCommitUpdatedArgs, 'streamId'>>;
|
||||
ping?: SubscriptionResolver<ResolversTypes['String'], "ping", ParentType, ContextType>;
|
||||
projectAccSyncItemsUpdated?: SubscriptionResolver<ResolversTypes['String'], "projectAccSyncItemsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectAccSyncItemsUpdatedArgs, 'id'>>;
|
||||
projectAutomationsUpdated?: SubscriptionResolver<ResolversTypes['ProjectAutomationsUpdatedMessage'], "projectAutomationsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectAutomationsUpdatedArgs, 'projectId'>>;
|
||||
projectCommentsUpdated?: SubscriptionResolver<ResolversTypes['ProjectCommentsUpdatedMessage'], "projectCommentsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectCommentsUpdatedArgs, 'target'>>;
|
||||
projectFileImportUpdated?: SubscriptionResolver<ResolversTypes['ProjectFileImportUpdatedMessage'], "projectFileImportUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectFileImportUpdatedArgs, 'id'>>;
|
||||
@@ -7858,6 +7979,9 @@ export type WorkspaceUpdatedMessageResolvers<ContextType = GraphQLContext, Paren
|
||||
};
|
||||
|
||||
export type Resolvers<ContextType = GraphQLContext> = {
|
||||
AccSyncItem?: AccSyncItemResolvers<ContextType>;
|
||||
AccSyncItemCollection?: AccSyncItemCollectionResolvers<ContextType>;
|
||||
AccSyncItemMutations?: AccSyncItemMutationsResolvers<ContextType>;
|
||||
ActiveUserMutations?: ActiveUserMutationsResolvers<ContextType>;
|
||||
Activity?: ActivityResolvers<ContextType>;
|
||||
ActivityCollection?: ActivityCollectionResolvers<ContextType>;
|
||||
|
||||
@@ -20,6 +20,47 @@ export type Scalars = {
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
||||
export type AccSyncItem = {
|
||||
__typename?: 'AccSyncItem';
|
||||
accFileLineageId: Scalars['String']['output'];
|
||||
accHubId: Scalars['String']['output'];
|
||||
accProjectId: Scalars['String']['output'];
|
||||
accRootFolderUrn: Scalars['String']['output'];
|
||||
accWebhookId?: Maybe<Scalars['String']['output']>;
|
||||
author?: Maybe<LimitedUser>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
modelId: Scalars['String']['output'];
|
||||
projectId: Scalars['String']['output'];
|
||||
status: AccSyncItemStatus;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemCollection = {
|
||||
__typename?: 'AccSyncItemCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
items: Array<AccSyncItem>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemMutations = {
|
||||
__typename?: 'AccSyncItemMutations';
|
||||
create?: Maybe<AccSyncItem>;
|
||||
};
|
||||
|
||||
|
||||
export type AccSyncItemMutationsCreateArgs = {
|
||||
input: CreateAccSyncItemInput;
|
||||
};
|
||||
|
||||
export const AccSyncItemStatus = {
|
||||
Failed: 'FAILED',
|
||||
Paused: 'PAUSED',
|
||||
Sync: 'SYNC',
|
||||
Syncing: 'SYNCING'
|
||||
} as const;
|
||||
|
||||
export type AccSyncItemStatus = typeof AccSyncItemStatus[keyof typeof AccSyncItemStatus];
|
||||
export type ActiveUserMutations = {
|
||||
__typename?: 'ActiveUserMutations';
|
||||
emailMutations: UserEmailMutations;
|
||||
@@ -888,6 +929,15 @@ export type CountOnlyCollection = {
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type CreateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['String']['input'];
|
||||
accHubId: Scalars['String']['input'];
|
||||
accProjectId: Scalars['String']['input'];
|
||||
accRootFolderUrn: Scalars['String']['input'];
|
||||
modelId: Scalars['String']['input'];
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateAutomateFunctionInput = {
|
||||
description: Scalars['String']['input'];
|
||||
/** Base64 encoded image data string */
|
||||
@@ -963,6 +1013,11 @@ export type CurrencyBasedPrices = {
|
||||
usd: WorkspacePaidPlanPrices;
|
||||
};
|
||||
|
||||
export type DeleteAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
@@ -1448,6 +1503,7 @@ export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
/** The void stares back. */
|
||||
_?: Maybe<Scalars['String']['output']>;
|
||||
accSyncItemMutations: AccSyncItemMutations;
|
||||
/** Various Active User oriented mutations */
|
||||
activeUserMutations: ActiveUserMutations;
|
||||
admin: AdminMutations;
|
||||
@@ -2080,6 +2136,8 @@ export type Price = {
|
||||
|
||||
export type Project = {
|
||||
__typename?: 'Project';
|
||||
accSyncItem: AccSyncItem;
|
||||
accSyncItems: AccSyncItemCollection;
|
||||
allowPublicComments: Scalars['Boolean']['output'];
|
||||
/** Get a single automation by id. Error will be thrown if automation is not found or inaccessible. */
|
||||
automation: Automation;
|
||||
@@ -2141,6 +2199,11 @@ export type Project = {
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccSyncItemArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAutomationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3630,6 +3693,7 @@ export type Subscription = {
|
||||
* Note: Only works in test environment
|
||||
*/
|
||||
ping: Scalars['String']['output'];
|
||||
projectAccSyncItemsUpdated: Scalars['String']['output'];
|
||||
/** Subscribe to updates to automations in the project */
|
||||
projectAutomationsUpdated: ProjectAutomationsUpdatedMessage;
|
||||
/**
|
||||
@@ -3746,6 +3810,12 @@ export type SubscriptionCommitUpdatedArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAccSyncItemsUpdatedArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
itemIds?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAutomationsUpdatedArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3886,6 +3956,12 @@ export type TriggeredAutomationsStatus = {
|
||||
statusMessage?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UpdateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
status: AccSyncItemStatus;
|
||||
};
|
||||
|
||||
/** Any null values will be ignored */
|
||||
export type UpdateAutomateFunctionInput = {
|
||||
description?: InputMaybe<Scalars['String']['input']>;
|
||||
|
||||
@@ -81,6 +81,7 @@ const getEnabledModuleNames = () => {
|
||||
FF_GATEKEEPER_MODULE_ENABLED
|
||||
} = getFeatureFlags()
|
||||
const moduleNames = [
|
||||
'acc',
|
||||
'accessrequests',
|
||||
'activitystream',
|
||||
'apiexplorer',
|
||||
@@ -102,6 +103,7 @@ const getEnabledModuleNames = () => {
|
||||
'multiregion'
|
||||
]
|
||||
|
||||
// TODO: add acc with feature flag?
|
||||
if (FF_AUTOMATE_MODULE_ENABLED) moduleNames.push('automate')
|
||||
if (FF_GENDOAI_MODULE_ENABLED) moduleNames.push('gendo')
|
||||
// the order of the event listeners matters
|
||||
|
||||
@@ -53,6 +53,10 @@ import {
|
||||
fileuploadEventNamespace,
|
||||
FileuploadEventsPayloads
|
||||
} from '@/modules/fileuploads/domain/events'
|
||||
import {
|
||||
accSyncItemEventsNamespace,
|
||||
AccSyncItemEventsPayloads
|
||||
} from '@/modules/acc/domain/events'
|
||||
|
||||
type AllEventsWildcard = '**'
|
||||
type EventWildcard = '*'
|
||||
@@ -70,6 +74,7 @@ type TestEventsPayloads = {
|
||||
// we should only ever extend this type, other helper types will be derived from this
|
||||
type EventsByNamespace = {
|
||||
test: TestEventsPayloads
|
||||
[accSyncItemEventsNamespace]: AccSyncItemEventsPayloads
|
||||
[workspaceEventNamespace]: WorkspaceEventsPayloads
|
||||
[gatekeeperEventNamespace]: GatekeeperEventPayloads
|
||||
[serverinvitesEventNamespace]: ServerInvitesEventsPayloads
|
||||
|
||||
@@ -21,6 +21,47 @@ export type Scalars = {
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
||||
export type AccSyncItem = {
|
||||
__typename?: 'AccSyncItem';
|
||||
accFileLineageId: Scalars['String']['output'];
|
||||
accHubId: Scalars['String']['output'];
|
||||
accProjectId: Scalars['String']['output'];
|
||||
accRootFolderUrn: Scalars['String']['output'];
|
||||
accWebhookId?: Maybe<Scalars['String']['output']>;
|
||||
author?: Maybe<LimitedUser>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
modelId: Scalars['String']['output'];
|
||||
projectId: Scalars['String']['output'];
|
||||
status: AccSyncItemStatus;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemCollection = {
|
||||
__typename?: 'AccSyncItemCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
items: Array<AccSyncItem>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type AccSyncItemMutations = {
|
||||
__typename?: 'AccSyncItemMutations';
|
||||
create?: Maybe<AccSyncItem>;
|
||||
};
|
||||
|
||||
|
||||
export type AccSyncItemMutationsCreateArgs = {
|
||||
input: CreateAccSyncItemInput;
|
||||
};
|
||||
|
||||
export const AccSyncItemStatus = {
|
||||
Failed: 'FAILED',
|
||||
Paused: 'PAUSED',
|
||||
Sync: 'SYNC',
|
||||
Syncing: 'SYNCING'
|
||||
} as const;
|
||||
|
||||
export type AccSyncItemStatus = typeof AccSyncItemStatus[keyof typeof AccSyncItemStatus];
|
||||
export type ActiveUserMutations = {
|
||||
__typename?: 'ActiveUserMutations';
|
||||
emailMutations: UserEmailMutations;
|
||||
@@ -889,6 +930,15 @@ export type CountOnlyCollection = {
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type CreateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['String']['input'];
|
||||
accHubId: Scalars['String']['input'];
|
||||
accProjectId: Scalars['String']['input'];
|
||||
accRootFolderUrn: Scalars['String']['input'];
|
||||
modelId: Scalars['String']['input'];
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateAutomateFunctionInput = {
|
||||
description: Scalars['String']['input'];
|
||||
/** Base64 encoded image data string */
|
||||
@@ -964,6 +1014,11 @@ export type CurrencyBasedPrices = {
|
||||
usd: WorkspacePaidPlanPrices;
|
||||
};
|
||||
|
||||
export type DeleteAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
@@ -1449,6 +1504,7 @@ export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
/** The void stares back. */
|
||||
_?: Maybe<Scalars['String']['output']>;
|
||||
accSyncItemMutations: AccSyncItemMutations;
|
||||
/** Various Active User oriented mutations */
|
||||
activeUserMutations: ActiveUserMutations;
|
||||
admin: AdminMutations;
|
||||
@@ -2081,6 +2137,8 @@ export type Price = {
|
||||
|
||||
export type Project = {
|
||||
__typename?: 'Project';
|
||||
accSyncItem: AccSyncItem;
|
||||
accSyncItems: AccSyncItemCollection;
|
||||
allowPublicComments: Scalars['Boolean']['output'];
|
||||
/** Get a single automation by id. Error will be thrown if automation is not found or inaccessible. */
|
||||
automation: Automation;
|
||||
@@ -2142,6 +2200,11 @@ export type Project = {
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccSyncItemArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAutomationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3631,6 +3694,7 @@ export type Subscription = {
|
||||
* Note: Only works in test environment
|
||||
*/
|
||||
ping: Scalars['String']['output'];
|
||||
projectAccSyncItemsUpdated: Scalars['String']['output'];
|
||||
/** Subscribe to updates to automations in the project */
|
||||
projectAutomationsUpdated: ProjectAutomationsUpdatedMessage;
|
||||
/**
|
||||
@@ -3747,6 +3811,12 @@ export type SubscriptionCommitUpdatedArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAccSyncItemsUpdatedArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
itemIds?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionProjectAutomationsUpdatedArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
@@ -3887,6 +3957,12 @@ export type TriggeredAutomationsStatus = {
|
||||
statusMessage?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UpdateAccSyncItemInput = {
|
||||
accFileLineageId: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
status: AccSyncItemStatus;
|
||||
};
|
||||
|
||||
/** Any null values will be ignored */
|
||||
export type UpdateAutomateFunctionInput = {
|
||||
description?: InputMaybe<Scalars['String']['input']>;
|
||||
|
||||
Reference in New Issue
Block a user