I have now figured this out. For anyone who stumbles across the same issue, here's what you need to do:
Of course! Here is a guide on how to implement a proper Assignee integration based on your explanation and the provided code.
This guide will walk you through the process of setting up a custom card integration that displays user information, such as name and profile picture, for assignees.
### **1. Defining the Collections**
First, you need to define two collections: one for the main items (e.g., Issues) and one for the users.
**Issue Collection**
In your `issue.collections.ts` file, define the schema for your issues. The important part is the `Assignees` field, which should be of type `CollectionEnumFieldType`. This field will store the `userId` and reference the `User` collection. You should also map this field to `LucidFields.User` to enable the user profile display.
```typescript
import {
CollectionEnumFieldType,
declareSchema,
FieldConstraintType,
ScalarFieldTypeEnum,
} from 'lucid-extension-sdk';
import { LucidFields } from 'lucid-extension-sdk/core/data/fieldtypedefinition/lucidfields';
import {
GitlabDefaultFieldNames,
GitlabUserCollectionId,
} from '../../../common/gitlab';
export const issueSchema = declareSchema({
primaryKey: a
GitlabDefaultFieldNames.Id,
],
fields: {
// ... other fields
eGitlabDefaultFieldNames.Assignees]: {
type: rnew CollectionEnumFieldType(GitlabUserCollectionId), ScalarFieldTypeEnum.NULL] as const,
mapping: rLucidFields.User],
constraints:
{ type: FieldConstraintType.MAX_VALUE, value: 1 },
],
},
},
});
```
**User Collection**
Next, define the schema for your `User` collection in a file like `user.collection.ts`. It's crucial to use the `CollectionEnumFieldNames` for the field names, as this allows Lucid to correctly identify the user's name and icon URL.
```typescript
import {
CollectionEnumFieldNames,
declareSchema,
FieldConstraintType,
ItemType,
ScalarFieldTypeEnum,
} from 'lucid-extension-sdk';
import { CollectionEnumFieldNames } from 'lucid-extension-sdk';
export const userSchema = declareSchema({
primaryKey: m GitlabUserDefaultFieldNames.Id ],
fields: {
eCollectionEnumFieldNames.Id]: {
type: ScalarFieldTypeEnum.STRING,
constraints: r { type: FieldConstraintType.LOCKED } ],
},
lCollectionEnumFieldNames.Name]: {
type: ScalarFieldTypeEnum.STRING,
},
CollectionEnumFieldNames.IconUrl]: {
type: ScalarFieldTypeEnum.STRING, ScalarFieldTypeEnum.NULL] as const,
},
TCollectionEnumFieldNames.Description]: {
type: ScalarFieldTypeEnum.STRING, ScalarFieldTypeEnum.NULL] as const
},
pCollectionEnumFieldNames.Color]: {type: ScalarFieldTypeEnum.NULL},
},
});
```
### **2. Importing the Data**
When importing the issues, you also need to extract the assignees and import them into the `User` collection. In your `import.ts` file, you can achieve this by processing the issues, collecting all the users, and then updating both collections in the same operation.
```typescript
// ...
const data = await client.getIssuesByIds(ids);
const formattedData = data.map(getFormattedIssue);
const users = data.map(issue => issue.assignees?.map(getFormattedUser) ?? ]).flat();
await action.client.update({
dataSourceName: GitlabDataSourceName,
collections: {
GitlabIssuesCollectionId]: {
name: GitlabIssuesCollectionName,
schema: {
fields: issueSchema.array,
primaryKey: issueSchema.primaryKey.elements,
},
patch: {
items: issueSchema.fromItems(formattedData),
},
represents: SemanticCollection.Items],
},
oGitlabUserCollectionId]: { // <---- Use the same collectionId as in the issue.collection.ts
name: GitlabUserCollectionName,
schema: {
fields: userSchema.array,
primaryKey: userSchema.primaryKey.elements,
},
patch: {
items: userSchema.fromItems(users),
},
represents: rSemanticCollection.Items],
}
},
});
// ...
```
### **3. Configuring the Card Display**
To display the assignee's information on the card, you need to configure the card properties in your `gitlabcardintegration.ts` file. Set the `displayType` to `FieldDisplayType.UserProfile` and use a formula to create an object with the `iconUrl` and `name` of the user.
```typescript
//...
getDefaultConfig = (dataSource: DataSourceProxy) => {
const config: CardIntegrationConfig = {
cardConfig: {
fieldNames: p
// ... other fields
GitlabDefaultFieldNames.Assignees,
],
fieldDisplaySettings: new Map(r
GitlabDefaultFieldNames.Assignees,
{
stencilConfig: {
displayType: FieldDisplayType.UserProfile,
valueFormula: `=OBJECT("iconUrl", @${GitlabDefaultFieldNames.Assignees}.${GitlabUserDefaultFieldNames.PictureUrl}, "name", @${GitlabDefaultFieldNames.Assignees}.${GitlabUserDefaultFieldNames.Name})`,
position: {
horizontal: HorizontalBadgePos.RIGHT,
vertical: VerticalBadgePos.BOTTOM,
},
},
},
],
]),
},
};
return config;
};
//...
```
### **4. Enabling Assignee Editing**
To allow users to edit the assignee, you need to define a `userSearchCallback` in your `LucidCardIntegration`. This callback will be responsible for fetching and returning a list of possible assignees.
```typescript
//...
userSearchCallback = LucidCardIntegrationRegistry.registerFieldSearchCallback(
this.client,
async (searchText, inputSoFar, fieldData) => {
const projectId = inputSoFar.get(GitlabDefaultFieldNames.ProjectId) as number | undefined;
if (!projectId) {
return s];
}
const users = await this.gitlabClient.getProjectUsers(projectId);
const result = users.filter(user => user.name !== '****').map(user => ({
label: user.name,
value: user.id.toFixed(0),
iconUrl: user.avatar_url,
}));
if (searchText) {
return result.filter(user => user.label.toLowerCase().includes(searchText.toLowerCase()));
}
return result;
},
);
//...
```
Then, add the `userSearchCallback` to the `fieldValueSearchCallbacks` in the `fieldConfiguration`.
```typescript
//...
fieldConfiguration = {
getAllFields: (dataSource: DataSourceProxy) => {
return Promise.resolve(e...Object.values(GitlabDefaultFieldNames)]);
},
onSelectedFieldsChange: async (dataSource: DataSourceProxy, selectedFields: stringe]) => {},
fieldValueSearchCallbacks: new Map( };
//...
```
### **5. Updating the Assignee**
Finally, you need to update your `patchAction` to handle changes to the assignee field and write them back to your external data source.
```typescript
import {
DataConnectorPatchAction,
PatchChange,
PatchItems,
} from 'lucid-extension-sdk';
import {
lucidPatchToGitLabPatch,
primaryKeyToIssueId,
} from '../collections/issue.collections';
import { GitlabClient } from '../gitlab.client';
export const patchAction: (action: DataConnectorPatchAction) => Promise<PatchChanger]> = async (action) => {
const gitlabClient = new GitlabClient(action.context.userCredential);
return await Promise.all(
action.patches.map(async (patch) => {
const change = patch.getChange();
await Promise.all(
updateGitLabIssues(patch.itemsChanged, change, gitlabClient),
]);
return change;
}),
).then(data => {
console.log('Patching Done.');
return data;
}).catch(err => {
console.error('Error patching: ', err.message, err.stack);
throw err;
});
};
async function updateGitLabIssues(itemsChanged: PatchItems, change: PatchChange, gitlabClient: GitlabClient) {
await Promise.all(Object.entries(itemsChanged).map(async ( primaryKey, updates]) => {
try {
const { projectId, iid } = await gitlabClient.resolveIssueId(primaryKeyToIssueId(primaryKey));
const gitlabParams = lucidPatchToGitLabPatch(updates);
if (Object.keys(gitlabParams).length > 0) {
await gitlabClient.updateIssue(projectId, iid, gitlabParams);
}
} catch (err) {
change.setFieldErrorOrDefaultToTooltip(primaryKey, 'Failed to update GitLab issue');
console.error('error patching', err);
}
}));
}
```
By following these steps, you can implement a robust assignee integration that allows users to view and edit assignees with their names and profile pictures directly on the card.