Skip to content

Add space-configure-from-template plugin#737

Merged
mmccooyyy merged 7 commits intomainfrom
space-configure-from-template
Jan 22, 2025
Merged

Add space-configure-from-template plugin#737
mmccooyyy merged 7 commits intomainfrom
space-configure-from-template

Conversation

@mmccooyyy
Copy link
Contributor

Please explain how to summarize this PR for the Changelog:

Tell code reviewer how and what to test:

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2025

Walkthrough

The pull request introduces the @flatfile/plugin-space-configure-from-template plugin, which facilitates the creation of a new Flatfile Space based on a predefined Space Template. The plugin operates through a server-side listener that responds to the space:configure event. It includes a higher-order function for configuring spaces, documentation with usage examples, and configuration files for bundling and testing. Additionally, an end-to-end test suite is provided to validate the plugin's functionality, ensuring that the configuration process is automated and reliable.

Changes

File Change Summary
README.md Added documentation for the new Space Configuration from Template plugin, including usage examples and plugin purpose.
src/index.ts Re-exports all entities from ./space.configure.from.template.
src/space.configure.from.template.ts Implemented configureSpaceFromTemplate higher-order function for Space configuration using Flatfile API.
tsup.config.mjs Added bundler configuration using @flatfile/bundler-config-tsup.
vitest.config.ts Added Vitest configuration using @flatfile/config-vitest.
src/space.configure.from.template.e2e.spec.ts Created end-to-end test suite for the Space Configuration plugin.

Sequence Diagram

sequenceDiagram
    participant Listener as Space Listener
    participant Plugin as ConfigureSpaceFromTemplate
    participant API as Flatfile API
    
    Listener->>Plugin: Trigger 'space:configure' event
    Plugin->>API: Retrieve Space Templates
    API-->>Plugin: Return Templates
    Plugin->>API: Select Oldest Template
    Plugin->>API: Fetch Template Workbooks
    Plugin->>API: Create Workbooks in New Space
    Plugin->>API: Update Space Metadata
    Plugin->>API: Create Associated Documents
    Plugin->>Listener: Execute Callback Function
Loading

The sequence diagram illustrates the core workflow of the configureSpaceFromTemplate plugin, showing how it interacts with the Flatfile API to configure a new Space based on an existing template.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
plugins/space-configure-from-template/src/space.configure.from.template.ts (2)

81-81: Use meaningful progress messages with tick function

The progress message 'Workbook created' at 50% might be misleading if multiple workbooks are being created. Consider updating the message to reflect the actual progress or specifying how many workbooks have been created.

Apply this diff to improve the progress message:

-      await tick(50, 'Workbook created')
+      await tick(50, 'Workbooks created')
🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.


1-105: Address code formatting issues

The Prettier formatting check failed in the CI pipeline. Please run Prettier with the --write flag to automatically format the code according to the project's style guidelines.

🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.

plugins/space-configure-from-template/src/space.configure.e2e.spec.ts (2)

41-41: Await driver.shutdown() to ensure proper teardown

The driver.shutdown() function may be asynchronous. To guarantee that the driver shuts down completely before moving on, consider awaiting this operation.

Apply this diff to await the shutdown:

-    driver.shutdown()
+    await driver.shutdown()

48-50: Reset mocks in afterEach hook

While listener.reset() clears the listener state, it doesn't reset the mock functions like mockFn. Consider adding vi.clearAllMocks() in the afterEach hook to ensure that mocks are reset between tests.

Apply this diff to reset mocks:

    afterEach(() => {
      listener.reset()
+     vi.clearAllMocks()
    })
plugins/space-configure-from-template/README.md (1)

16-16: Adjust heading levels to maintain proper hierarchy

The headings at lines 16, 35, and 56 are level 4 (####), but they should be level 3 (###) to maintain a proper heading structure, as they follow a level 2 heading.

Apply this diff to fix the heading levels:

-#### `callback` - `function`
+### `callback` - `function`

-#### Install
+### Install

-#### Example with Callback
+### Example with Callback

Also applies to: 35-35, 56-56

🧰 Tools
🪛 Markdownlint (0.37.0)

16-16: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63dd5e2 and fac647a.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json, !**/*.json
  • plugins/space-configure-from-template/package.json is excluded by !**/*.json
📒 Files selected for processing (6)
  • plugins/space-configure-from-template/README.md (1 hunks)
  • plugins/space-configure-from-template/src/index.ts (1 hunks)
  • plugins/space-configure-from-template/src/space.configure.e2e.spec.ts (1 hunks)
  • plugins/space-configure-from-template/src/space.configure.from.template.ts (1 hunks)
  • plugins/space-configure-from-template/tsup.config.mjs (1 hunks)
  • plugins/space-configure-from-template/vitest.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • plugins/space-configure-from-template/src/index.ts
  • plugins/space-configure-from-template/tsup.config.mjs
  • plugins/space-configure-from-template/vitest.config.ts
🧰 Additional context used
🪛 GitHub Actions: Lint
plugins/space-configure-from-template/src/space.configure.from.template.ts

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.

🪛 LanguageTool
plugins/space-configure-from-template/README.md

[misspelling] ~33-~33: This word is normally spelled as one.
Context: ... Designed for server-side listeners, it auto-configures the Space using the supplied settings. ...

(EN_COMPOUNDS_AUTO_CONFIGURES)

🪛 Markdownlint (0.37.0)
plugins/space-configure-from-template/README.md

16-16: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


35-35: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


56-56: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Setup
  • GitHub Check: Nullify Dependencies
🔇 Additional comments (1)
plugins/space-configure-from-template/README.md (1)

33-33: Correct the spelling of 'auto-configures'

The term "auto-configures" is typically spelled as a single word without a hyphen: "autoconfigures".

[typo]

Apply this diff to fix the spelling:

-Designed for server-side listeners, it auto-configures the Space using the supplied settings.
+Designed for server-side listeners, it autoconfigures the Space using the supplied settings.
🧰 Tools
🪛 LanguageTool

[misspelling] ~33-~33: This word is normally spelled as one.
Context: ... Designed for server-side listeners, it auto-configures the Space using the supplied settings. ...

(EN_COMPOUNDS_AUTO_CONFIGURES)

….template.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
plugins/space-configure-from-template/src/space.configure.from.template.ts (5)

1-6: Consider dependency injection for the API client.

Instead of creating a global API client instance, consider injecting it as a parameter to allow for better testing and flexibility.

-const api = new FlatfileClient()
+export function configureSpaceFromTemplate(
+  options: {
+    api?: FlatfileClient;
+    callback?: (event: FlatfileEvent, workbookIds: string[], tick: TickFunction) => any | Promise<any>;
+  }
+) {
+  const api = options.api || new FlatfileClient()
🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.


8-22: Update JSDoc to match implementation.

The documentation mentions a "setup factory" and "setup configuration" which aren't part of the current implementation. Please update the documentation to accurately reflect the template-based configuration approach.

🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.


23-29: Consider stricter typing for callback return value.

The callback's return type any | Promise<any> is very permissive. Consider using void | Promise<void> if the return value isn't used, or define a specific return type if it is.

🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.


48-50: Reconsider template selection strategy.

Always selecting the oldest template might not be the best strategy. Consider:

  1. Adding a parameter to specify the template ID
  2. Adding metadata to identify the default template
  3. Using the most recently updated template instead
🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.


98-102: Improve callback execution handling.

Consider adding:

  1. Error handling for callback execution
  2. Final progress update after successful completion
  3. More detailed return information
-        if (callback) {
-          await callback(event, workbookIds, tick)
-        }
-        return { info: 'Space configured' }
+        try {
+          if (callback) {
+            await callback(event, workbookIds, tick)
+          }
+          await tick(100, 'Configuration completed')
+          return {
+            info: 'Space configured',
+            workbookIds,
+            spaceId,
+            templateId: spaceTemplateId
+          }
+        } catch (error) {
+          throw new Error(`Callback execution failed: ${error.message}`)
+        }
🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fac647a and 6ab1073.

📒 Files selected for processing (1)
  • plugins/space-configure-from-template/src/space.configure.from.template.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Lint
plugins/space-configure-from-template/src/space.configure.from.template.ts

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Setup
  • GitHub Check: Nullify Dependencies
🔇 Additional comments (1)
plugins/space-configure-from-template/src/space.configure.from.template.ts (1)

37-42: Update isCollaborative to isTemplate.

The TODO comment indicates that isCollaborative should be changed to isTemplate.

🧰 Tools
🪛 GitHub Actions: Lint

[warning] Code style issues found. Prettier formatting check failed. Run Prettier with --write to fix.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
plugins/space-configure-from-template/src/space.configure.from.template.ts (2)

1-6: Consider injecting FlatfileClient for better testability.

Creating the API client at the module level makes the code harder to test. Consider passing it as a parameter to the configureSpaceFromTemplate function.

-const api = new FlatfileClient()
+export function configureSpaceFromTemplate(
+  callback?: (event: FlatfileEvent, workbookIds: string[], tick: TickFunction) => any | Promise<any>,
+  api: FlatfileClient = new FlatfileClient()
+) {

44-46: Enhance error handling with more context.

The error message could be more descriptive by including the app namespace.

-          throw new Error('No space template found')
+          throw new Error(`No space template found for namespace: ${app.data.namespace}`)
plugins/space-configure-from-template/src/space.configure.from.template.e2e.spec.ts (1)

28-36: Add error handling to test setup.

The setup process should handle potential failures gracefully to ensure proper cleanup.

   beforeAll(async () => {
-    await driver.start()
-    listener.mount(driver)
+    try {
+      await driver.start()
+      listener.mount(driver)
 
-    listener.use(configureSpaceFromTemplate(mockFn))
+      listener.use(configureSpaceFromTemplate(mockFn))
 
-    const space = await setupSpace()
-    spaceId = space.id
+      const space = await setupSpace()
+      spaceId = space.id
+    } catch (error) {
+      console.error('Test setup failed:', error)
+      throw error
+    }
   })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ab1073 and df59683.

📒 Files selected for processing (2)
  • plugins/space-configure-from-template/src/space.configure.from.template.e2e.spec.ts (1 hunks)
  • plugins/space-configure-from-template/src/space.configure.from.template.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Setup
  • GitHub Check: Lint
  • GitHub Check: Nullify Dependencies
🔇 Additional comments (4)
plugins/space-configure-from-template/src/space.configure.from.template.ts (4)

37-42: Update isCollaborative to isTemplate.

The TODO comment indicates this needs to be updated.


84-89: 🛠️ Refactor suggestion

Add validation and error handling for space metadata update.

The space update should validate the settings object and handle potential API errors.

-        await api.spaces.update(spaceId, {
-          primaryWorkbookId:
-            workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
-          settings: spaceTemplate.settings,
-        })
+        try {
+          if (!workbookIds?.length) {
+            throw new Error('No workbooks were created')
+          }
+          await api.spaces.update(spaceId, {
+            primaryWorkbookId: workbookIds[0],
+            settings: spaceTemplate.settings || {}
+          })
+          await tick(75, 'Space metadata updated')
+        } catch (error) {
+          throw new Error(`Failed to update space metadata: ${error.message}`)
+        }

Likely invalid or redundant comment.


74-82: 🛠️ Refactor suggestion

Improve workbook creation error handling and progress reporting.

The current implementation doesn't handle individual workbook creation failures and provides only a single progress update.

 const workbookIds = await Promise.all(
-  workbookConfigs.map(async (workbookConfig) => {
-    const workbook = await api.workbooks.create(workbookConfig)
-    return workbook.data.id
+  workbookConfigs.map(async (workbookConfig, index) => {
+    try {
+      const workbook = await api.workbooks.create(workbookConfig)
+      await tick(25 + (25 * index / workbookConfigs.length), 
+        `Created workbook ${index + 1} of ${workbookConfigs.length}`)
+      return workbook.data.id
+    } catch (error) {
+      throw new Error(`Failed to create workbook ${index + 1}: ${error.message}`)
+    }
   })
 )
-await tick(50, 'Workbook created')

Likely invalid or redundant comment.


91-98: 🛠️ Refactor suggestion

Improve document handling with pagination and progress updates.

The current implementation doesn't handle pagination for document listing and lacks progress updates during document creation.

-        const documents = await api.documents.list(spaceTemplateId)
+        const allDocuments: Flatfile.Document[] = []
+        let page = 1
+        while (true) {
+          const documents = await api.documents.list(spaceTemplateId, { page })
+          if (documents.data.length === 0) break
+          allDocuments.push(...documents.data)
+          page++
+        }
 
-        for (const document of documents.data) {
-          await api.documents.create(spaceId, document)
+        for (const [index, document] of allDocuments.entries()) {
+          try {
+            await api.documents.create(spaceId, document)
+            await tick(75 + (20 * index / allDocuments.length),
+              `Created document ${index + 1} of ${allDocuments.length}`)
+          } catch (error) {
+            throw new Error(`Failed to create document: ${error.message}`)
+          }
         }

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
plugins/space-configure-from-template/src/space.configure.from.template.ts (3)

6-6: Consider dependency injection for the API client.

Instead of creating a global API client instance, consider passing it as a parameter to the configureSpaceFromTemplate function. This would improve testability and allow for better dependency management.

-const api = new FlatfileClient()
-
-export function configureSpaceFromTemplate(
+export function configureSpaceFromTemplate(
+  api: FlatfileClient = new FlatfileClient(),
   callback?: (
     event: FlatfileEvent,
     workbookIds: string[],
     tick: TickFunction
   ) => any | Promise<any>
 )

8-29: Enhance documentation and type safety.

  1. Add usage examples in the JSDoc to demonstrate the callback pattern.
  2. Consider using a more specific return type for the callback instead of any.
 /**
  * `configureSpaceFromTemplate` is a higher-order function that creates a plugin to configure a
  * workspace using the Flatfile API. This function takes a setup factory that may either
  * be a static setup configuration or a function that creates a setup configuration
  * given a FlatfileEvent.
  *
  * When the job:ready for space:configure is emitted, the plugin will extract the necessary IDs, create a
  * configuration using the provided setup factory, create a workbook with this configuration,
  * and update the space with the workbook ID as the primary workbook ID.
  *
  * @param {Function} callback - A callback function that is called after the workbook is created.
  *
+ * @example
+ * ```typescript
+ * const plugin = configureSpaceFromTemplate(async (event, workbookIds, tick) => {
+ *   await tick(90, 'Performing post-configuration tasks');
+ *   // Custom logic here
+ * });
+ * ```
  *
  * @returns {Function} Returns a function that takes a FlatfileListener, adding a "space:configure" event listener
  * and configuring the space when the event is emitted.
  */
-  callback?: (event: FlatfileEvent, workbookIds: string[], tick: TickFunction) => any | Promise<any>
+  callback?: (event: FlatfileEvent, workbookIds: string[], tick: TickFunction) => void | Promise<void>

108-108: Enhance the response message with relevant details.

The current success message could be more informative by including details about the configured space.

-        return { info: 'Space configured' }
+        return {
+          info: 'Space configured successfully',
+          details: {
+            spaceId,
+            workbookCount: workbookIds.length,
+            documentCount: documents.data.length,
+            primaryWorkbookId: workbookIds[0] || null
+          }
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fddfe9 and 3ffda20.

📒 Files selected for processing (1)
  • plugins/space-configure-from-template/src/space.configure.from.template.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Setup
  • GitHub Check: Nullify Dependencies
🔇 Additional comments (1)
plugins/space-configure-from-template/src/space.configure.from.template.ts (1)

39-39: Update isCollaborative to isTemplate

There's a TODO comment indicating that isCollaborative should be changed to isTemplate. If isTemplate is now available, please update the code to reflect this change to ensure the plugin functions correctly.

Comment on lines +85 to +95
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
settings: spaceTemplate.settings,
metadata: spaceTemplate.metadata,
actions: spaceTemplate.actions,
access: spaceTemplate.access,
labels: spaceTemplate.labels,
translationsPath: spaceTemplate.translationsPath,
languageOverride: spaceTemplate.languageOverride,
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add property validation before copying template properties.

The code assumes all properties from the template are safe to copy. Consider adding validation and using optional chaining to handle potentially undefined properties safely.

 await api.spaces.update(spaceId, {
   primaryWorkbookId:
     workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
-  settings: spaceTemplate.settings,
-  metadata: spaceTemplate.metadata,
-  actions: spaceTemplate.actions,
-  access: spaceTemplate.access,
-  labels: spaceTemplate.labels,
-  translationsPath: spaceTemplate.translationsPath,
-  languageOverride: spaceTemplate.languageOverride,
+  ...(spaceTemplate.settings && { settings: spaceTemplate.settings }),
+  ...(spaceTemplate.metadata && { metadata: spaceTemplate.metadata }),
+  ...(spaceTemplate.actions && { actions: spaceTemplate.actions }),
+  ...(spaceTemplate.access && { access: spaceTemplate.access }),
+  ...(spaceTemplate.labels && { labels: spaceTemplate.labels }),
+  ...(spaceTemplate.translationsPath && { translationsPath: spaceTemplate.translationsPath }),
+  ...(spaceTemplate.languageOverride && { languageOverride: spaceTemplate.languageOverride }),
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
settings: spaceTemplate.settings,
metadata: spaceTemplate.metadata,
actions: spaceTemplate.actions,
access: spaceTemplate.access,
labels: spaceTemplate.labels,
translationsPath: spaceTemplate.translationsPath,
languageOverride: spaceTemplate.languageOverride,
})
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
...(spaceTemplate.settings && { settings: spaceTemplate.settings }),
...(spaceTemplate.metadata && { metadata: spaceTemplate.metadata }),
...(spaceTemplate.actions && { actions: spaceTemplate.actions }),
...(spaceTemplate.access && { access: spaceTemplate.access }),
...(spaceTemplate.labels && { labels: spaceTemplate.labels }),
...(spaceTemplate.translationsPath && { translationsPath: spaceTemplate.translationsPath }),
...(spaceTemplate.languageOverride && { languageOverride: spaceTemplate.languageOverride }),
})

Comment on lines 32 to 109
jobHandler('space:configure', async (event, tick) => {
const { spaceId, environmentId, appId } = event.context
const app = await api.apps.get(appId)

// Get all the space templates for the app sorted by creation date, oldest first
const spaceTemplates = await api.spaces.list({
namespace: app.data.namespace,
isCollaborative: true, // TODO: change to isTemplate
sortField: 'createdAt',
sortDirection: 'asc',
})

if (spaceTemplates.data.length === 0) {
throw new Error('No space template found')
}

// Get the oldest space template
const spaceTemplate = spaceTemplates.data[0]
const spaceTemplateId = spaceTemplate.id

// Get all the workbooks for the space template
const workbooks = await api.workbooks.list({
spaceId: spaceTemplateId,
})

// Convert workbooks from the template to workbook configs
const workbookConfigs: Flatfile.CreateWorkbookConfig[] =
workbooks.data.map((workbook) => ({
name: workbook.name,
labels: workbook.labels,
spaceId: spaceId,
environmentId: environmentId,
namespace: app.data.namespace,
sheets: workbook.sheets.map((sheet) => ({
...sheet.config,
})),
actions: workbook.actions,
settings: workbook.settings,
metadata: workbook.metadata,
treatments: workbook.treatments,
}))

// Create the workbooks
const workbookIds = await Promise.all(
workbookConfigs.map(async (workbookConfig) => {
const workbook = await api.workbooks.create(workbookConfig)
return workbook.data.id
})
)

await tick(50, 'Workbook created')

// Set some metadata on the space
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
settings: spaceTemplate.settings,
metadata: spaceTemplate.metadata,
actions: spaceTemplate.actions,
access: spaceTemplate.access,
labels: spaceTemplate.labels,
translationsPath: spaceTemplate.translationsPath,
languageOverride: spaceTemplate.languageOverride,
})

// Get all the documents for the space template...
const documents = await api.documents.list(spaceTemplateId)

// ...and create them in the new space
for (const document of documents.data) {
await api.documents.create(spaceId, document)
}

if (callback) {
await callback(event, workbookIds, tick)
}
return { info: 'Space configured' }
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement comprehensive error handling.

The job handler would benefit from a try-catch block with proper error reporting and cleanup in case of failures at any stage (template retrieval, workbook creation, document copying).

       jobHandler('space:configure', async (event, tick) => {
+        try {
           const { spaceId, environmentId, appId } = event.context
           // ... existing code ...
           return { info: 'Space configured' }
+        } catch (error) {
+          const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
+          await tick(100, `Configuration failed: ${errorMessage}`)
+          throw new Error(`Failed to configure space from template: ${errorMessage}`)
+        }
       })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jobHandler('space:configure', async (event, tick) => {
const { spaceId, environmentId, appId } = event.context
const app = await api.apps.get(appId)
// Get all the space templates for the app sorted by creation date, oldest first
const spaceTemplates = await api.spaces.list({
namespace: app.data.namespace,
isCollaborative: true, // TODO: change to isTemplate
sortField: 'createdAt',
sortDirection: 'asc',
})
if (spaceTemplates.data.length === 0) {
throw new Error('No space template found')
}
// Get the oldest space template
const spaceTemplate = spaceTemplates.data[0]
const spaceTemplateId = spaceTemplate.id
// Get all the workbooks for the space template
const workbooks = await api.workbooks.list({
spaceId: spaceTemplateId,
})
// Convert workbooks from the template to workbook configs
const workbookConfigs: Flatfile.CreateWorkbookConfig[] =
workbooks.data.map((workbook) => ({
name: workbook.name,
labels: workbook.labels,
spaceId: spaceId,
environmentId: environmentId,
namespace: app.data.namespace,
sheets: workbook.sheets.map((sheet) => ({
...sheet.config,
})),
actions: workbook.actions,
settings: workbook.settings,
metadata: workbook.metadata,
treatments: workbook.treatments,
}))
// Create the workbooks
const workbookIds = await Promise.all(
workbookConfigs.map(async (workbookConfig) => {
const workbook = await api.workbooks.create(workbookConfig)
return workbook.data.id
})
)
await tick(50, 'Workbook created')
// Set some metadata on the space
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
settings: spaceTemplate.settings,
metadata: spaceTemplate.metadata,
actions: spaceTemplate.actions,
access: spaceTemplate.access,
labels: spaceTemplate.labels,
translationsPath: spaceTemplate.translationsPath,
languageOverride: spaceTemplate.languageOverride,
})
// Get all the documents for the space template...
const documents = await api.documents.list(spaceTemplateId)
// ...and create them in the new space
for (const document of documents.data) {
await api.documents.create(spaceId, document)
}
if (callback) {
await callback(event, workbookIds, tick)
}
return { info: 'Space configured' }
})
jobHandler('space:configure', async (event, tick) => {
try {
const { spaceId, environmentId, appId } = event.context
const app = await api.apps.get(appId)
// Get all the space templates for the app sorted by creation date, oldest first
const spaceTemplates = await api.spaces.list({
namespace: app.data.namespace,
isCollaborative: true, // TODO: change to isTemplate
sortField: 'createdAt',
sortDirection: 'asc',
})
if (spaceTemplates.data.length === 0) {
throw new Error('No space template found')
}
// Get the oldest space template
const spaceTemplate = spaceTemplates.data[0]
const spaceTemplateId = spaceTemplate.id
// Get all the workbooks for the space template
const workbooks = await api.workbooks.list({
spaceId: spaceTemplateId,
})
// Convert workbooks from the template to workbook configs
const workbookConfigs: Flatfile.CreateWorkbookConfig[] =
workbooks.data.map((workbook) => ({
name: workbook.name,
labels: workbook.labels,
spaceId: spaceId,
environmentId: environmentId,
namespace: app.data.namespace,
sheets: workbook.sheets.map((sheet) => ({
...sheet.config,
})),
actions: workbook.actions,
settings: workbook.settings,
metadata: workbook.metadata,
treatments: workbook.treatments,
}))
// Create the workbooks
const workbookIds = await Promise.all(
workbookConfigs.map(async (workbookConfig) => {
const workbook = await api.workbooks.create(workbookConfig)
return workbook.data.id
})
)
await tick(50, 'Workbook created')
// Set some metadata on the space
await api.spaces.update(spaceId, {
primaryWorkbookId:
workbookIds && workbookIds.length > 0 ? workbookIds[0] : '',
settings: spaceTemplate.settings,
metadata: spaceTemplate.metadata,
actions: spaceTemplate.actions,
access: spaceTemplate.access,
labels: spaceTemplate.labels,
translationsPath: spaceTemplate.translationsPath,
languageOverride: spaceTemplate.languageOverride,
})
// Get all the documents for the space template...
const documents = await api.documents.list(spaceTemplateId)
// ...and create them in the new space
for (const document of documents.data) {
await api.documents.create(spaceId, document)
}
if (callback) {
await callback(event, workbookIds, tick)
}
return { info: 'Space configured' }
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
await tick(100, `Configuration failed: ${errorMessage}`)
throw new Error(`Failed to configure space from template: ${errorMessage}`)
}
})

Copy link
Contributor

@ncronquist ncronquist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved pending isAppTemplate change.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
plugins/space-configure-from-template/src/space.configure.from.template.ts (4)

6-6: Consider injecting the API client instead of using a global instance.

Creating a global API client instance makes the code harder to test and less flexible. Consider passing the client as a parameter to improve testability and dependency management.

-const api = new FlatfileClient()
+export function configureSpaceFromTemplate(
+  options: {
+    api?: FlatfileClient,
+    callback?: (event: FlatfileEvent, workbookIds: string[], tick: TickFunction) => any | Promise<any>
+  }
+) {
+  const api = options.api || new FlatfileClient()

8-22: Update documentation to match implementation.

The JSDoc mentions concepts like "setup factory" and "setup configuration" that aren't used in the actual implementation. Please revise the documentation to accurately reflect the current functionality.


23-29: Add explicit return type for better type safety.

Consider adding an explicit return type to improve type safety and documentation.

 export function configureSpaceFromTemplate(
   callback?: (
     event: FlatfileEvent,
     workbookIds: string[],
     tick: TickFunction
   ) => any | Promise<any>
-) {
+): (listener: FlatfileListener) => void {

1-112: Consider implementing a more robust error recovery mechanism.

The plugin would benefit from a comprehensive error recovery strategy that includes:

  1. Rolling back created resources on failure
  2. Retrying failed operations with exponential backoff
  3. Maintaining an audit log of actions
  4. Implementing proper cleanup on partial failures

This would make the plugin more resilient in production environments.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ffda20 and a31eaee.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json, !**/*.json
  • package.json is excluded by !**/*.json
📒 Files selected for processing (1)
  • plugins/space-configure-from-template/src/space.configure.from.template.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Setup
  • GitHub Check: Lint
  • GitHub Check: Nullify Dependencies

@mmccooyyy mmccooyyy merged commit b1fa42b into main Jan 22, 2025
37 checks passed
@mmccooyyy mmccooyyy deleted the space-configure-from-template branch January 22, 2025 02:26
This was referenced Feb 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants