@memberjunction/server 4.3.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skip-agent.js +9 -9
- package/dist/agents/skip-agent.js.map +1 -1
- package/dist/apolloServer/TransactionPlugin.d.ts +4 -0
- package/dist/apolloServer/TransactionPlugin.d.ts.map +1 -0
- package/dist/apolloServer/TransactionPlugin.js +46 -0
- package/dist/apolloServer/TransactionPlugin.js.map +1 -0
- package/dist/auth/APIKeyScopeAuth.js.map +1 -1
- package/dist/auth/__tests__/backward-compatibility.test.d.ts +2 -0
- package/dist/auth/__tests__/backward-compatibility.test.d.ts.map +1 -0
- package/dist/auth/__tests__/backward-compatibility.test.js +135 -0
- package/dist/auth/__tests__/backward-compatibility.test.js.map +1 -0
- package/dist/auth/exampleNewUserSubClass.d.ts +2 -2
- package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/newUsers.d.ts +2 -2
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +6 -6
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.d.ts +2 -2
- package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.js +3 -3
- package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
- package/dist/generated/generated.d.ts +10724 -10721
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +48710 -48698
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/DeleteOptionsInput.d.ts +12 -2
- package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
- package/dist/generic/DeleteOptionsInput.js +12 -2
- package/dist/generic/DeleteOptionsInput.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +6 -6
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.js +3 -3
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +123 -0
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -0
- package/dist/resolvers/AskSkipResolver.js +1788 -0
- package/dist/resolvers/AskSkipResolver.js.map +1 -0
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts +3 -3
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +19 -19
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/EntityResolver.js +1 -1
- package/dist/resolvers/EntityResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +4 -4
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.js +20 -20
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/ISAEntityResolver.d.ts +22 -1
- package/dist/resolvers/ISAEntityResolver.d.ts.map +1 -1
- package/dist/resolvers/ISAEntityResolver.js +77 -1
- package/dist/resolvers/ISAEntityResolver.js.map +1 -1
- package/dist/resolvers/ReportResolver.js +3 -3
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +3 -3
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +1 -1
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +2 -2
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +1 -1
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +9 -9
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +10 -10
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.js +2 -2
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/resolvers/UserResolver.js +8 -8
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.js +5 -5
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +1 -1
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts +4 -0
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -0
- package/dist/scheduler/LearningCycleScheduler.js +4 -0
- package/dist/scheduler/LearningCycleScheduler.js.map +1 -0
- package/dist/services/TaskOrchestrator.d.ts +1 -1
- package/dist/services/TaskOrchestrator.js +5 -5
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/package.json +52 -52
- package/src/agents/skip-agent.ts +9 -9
- package/src/auth/APIKeyScopeAuth.ts +5 -5
- package/src/auth/exampleNewUserSubClass.ts +2 -2
- package/src/auth/index.ts +3 -3
- package/src/auth/newUsers.ts +9 -9
- package/src/entitySubclasses/entityPermissions.server.ts +3 -3
- package/src/generated/generated.ts +31186 -31177
- package/src/generic/DeleteOptionsInput.ts +12 -2
- package/src/generic/ResolverBase.ts +7 -7
- package/src/generic/RunViewResolver.ts +3 -3
- package/src/resolvers/APIKeyResolver.ts +2 -2
- package/src/resolvers/ComponentRegistryResolver.ts +9 -9
- package/src/resolvers/CreateQueryResolver.ts +18 -18
- package/src/resolvers/EntityResolver.ts +1 -1
- package/src/resolvers/FileCategoryResolver.ts +5 -5
- package/src/resolvers/FileResolver.ts +27 -27
- package/src/resolvers/GetDataContextDataResolver.ts +2 -2
- package/src/resolvers/ISAEntityResolver.ts +77 -1
- package/src/resolvers/ReportResolver.ts +4 -4
- package/src/resolvers/RunAIAgentResolver.ts +7 -7
- package/src/resolvers/RunAIPromptResolver.ts +1 -1
- package/src/resolvers/RunTemplateResolver.ts +4 -4
- package/src/resolvers/SyncDataResolver.ts +3 -3
- package/src/resolvers/SyncRolesUsersResolver.ts +26 -26
- package/src/resolvers/UserFavoriteResolver.ts +2 -2
- package/src/resolvers/UserResolver.ts +8 -8
- package/src/resolvers/UserViewResolver.ts +6 -6
- package/src/rest/ViewOperationsHandler.ts +1 -1
- package/src/services/TaskOrchestrator.ts +31 -31
package/src/agents/skip-agent.ts
CHANGED
|
@@ -128,7 +128,7 @@ export class SkipProxyAgent extends BaseAgent {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
// Load conversation messages from database if conversationId is provided
|
|
131
|
-
// This ensures we get real UUIDs from
|
|
131
|
+
// This ensures we get real UUIDs from MJConversationDetailEntity records
|
|
132
132
|
let skipMessages: SkipMessage[];
|
|
133
133
|
if (conversationId && params.contextUser) {
|
|
134
134
|
skipMessages = await this.loadMessagesFromDatabase(conversationId, params.contextUser);
|
|
@@ -204,7 +204,7 @@ export class SkipProxyAgent extends BaseAgent {
|
|
|
204
204
|
try {
|
|
205
205
|
const rv = new RunView();
|
|
206
206
|
const result = await rv.RunView({
|
|
207
|
-
EntityName: 'Conversation Details',
|
|
207
|
+
EntityName: 'MJ: Conversation Details',
|
|
208
208
|
ExtraFilter: `ConversationID='${conversationId}'`,
|
|
209
209
|
OrderBy: '__mj_CreatedAt ASC'
|
|
210
210
|
}, contextUser);
|
|
@@ -458,7 +458,7 @@ const demoSpecJson = {
|
|
|
458
458
|
"usageContext": "Main entity list display and filtering"
|
|
459
459
|
},
|
|
460
460
|
{
|
|
461
|
-
"name": "Entity Fields",
|
|
461
|
+
"name": "MJ: Entity Fields",
|
|
462
462
|
"description": "Fields belonging to each entity",
|
|
463
463
|
"displayFields": [
|
|
464
464
|
"Name",
|
|
@@ -565,7 +565,7 @@ const demoSpecJson = {
|
|
|
565
565
|
"usageContext": "Details panel to show entity fields"
|
|
566
566
|
},
|
|
567
567
|
{
|
|
568
|
-
"name": "Entity Relationships",
|
|
568
|
+
"name": "MJ: Entity Relationships",
|
|
569
569
|
"description": "Relationships between entities",
|
|
570
570
|
"displayFields": [
|
|
571
571
|
"RelatedEntity",
|
|
@@ -760,7 +760,7 @@ const demoSpecJson = {
|
|
|
760
760
|
"usageContext": "Main entity list display and filtering"
|
|
761
761
|
},
|
|
762
762
|
{
|
|
763
|
-
"name": "Entity Fields",
|
|
763
|
+
"name": "MJ: Entity Fields",
|
|
764
764
|
"description": "Fields belonging to each entity",
|
|
765
765
|
"displayFields": [
|
|
766
766
|
"Name",
|
|
@@ -867,7 +867,7 @@ const demoSpecJson = {
|
|
|
867
867
|
"usageContext": "Details panel to show entity fields"
|
|
868
868
|
},
|
|
869
869
|
{
|
|
870
|
-
"name": "Entity Relationships",
|
|
870
|
+
"name": "MJ: Entity Relationships",
|
|
871
871
|
"description": "Relationships between entities",
|
|
872
872
|
"displayFields": [
|
|
873
873
|
"RelatedEntity",
|
|
@@ -1154,7 +1154,7 @@ const demoSpecJson = {
|
|
|
1154
1154
|
"usageContext": "Main entity list display and filtering"
|
|
1155
1155
|
},
|
|
1156
1156
|
{
|
|
1157
|
-
"name": "Entity Fields",
|
|
1157
|
+
"name": "MJ: Entity Fields",
|
|
1158
1158
|
"description": "Fields belonging to each entity",
|
|
1159
1159
|
"displayFields": [
|
|
1160
1160
|
"Name",
|
|
@@ -1261,7 +1261,7 @@ const demoSpecJson = {
|
|
|
1261
1261
|
"usageContext": "Details panel to show entity fields"
|
|
1262
1262
|
},
|
|
1263
1263
|
{
|
|
1264
|
-
"name": "Entity Relationships",
|
|
1264
|
+
"name": "MJ: Entity Relationships",
|
|
1265
1265
|
"description": "Relationships between entities",
|
|
1266
1266
|
"displayFields": [
|
|
1267
1267
|
"RelatedEntity",
|
|
@@ -1509,5 +1509,5 @@ const demoSpecJson = {
|
|
|
1509
1509
|
}
|
|
1510
1510
|
],
|
|
1511
1511
|
"libraries": [],
|
|
1512
|
-
"code": "function EntityBrowser({ utilities, styles, components, callbacks, savedUserSettings, onSaveUserSettings }) {\n // Extract child components\n const { EntityList, EntityDetails, EntityFilter } = components;\n \n // Initialize state from saved settings where appropriate\n const [selectedEntityId, setSelectedEntityId] = useState(savedUserSettings?.selectedEntityId);\n const [viewMode, setViewMode] = useState(savedUserSettings?.viewMode || 'grid');\n const [filters, setFilters] = useState(savedUserSettings?.filters || {});\n const [sortBy, setSortBy] = useState(savedUserSettings?.sortBy || 'Name');\n const [sortDirection, setSortDirection] = useState(savedUserSettings?.sortDirection || 'asc');\n const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(savedUserSettings?.filterPanelCollapsed || false);\n \n // Runtime UI state (not persisted)\n const [entities, setEntities] = useState([]);\n const [entityFields, setEntityFields] = useState([]);\n const [entityRelationships, setEntityRelationships] = useState([]);\n const [loading, setLoading] = useState(true);\n const [detailsPanelOpen, setDetailsPanelOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [uniqueSchemas, setUniqueSchemas] = useState([]);\n const [uniqueTables, setUniqueTables] = useState([]);\n \n // Load entities on mount and when filters/sort change\n useEffect(() => {\n const loadEntities = async () => {\n setLoading(true);\n try {\n // Build filter string\n let filterParts = [];\n if (filters.schema) {\n filterParts.push(`SchemaName = '${filters.schema}'`);\n }\n if (filters.table) {\n filterParts.push(`BaseTable = '${filters.table}'`);\n }\n if (searchQuery) {\n filterParts.push(`(Name LIKE '%${searchQuery}%' OR DisplayName LIKE '%${searchQuery}%' OR Description LIKE '%${searchQuery}%')`);\n }\n \n const result = await utilities.rv.RunView({\n EntityName: 'Entities',\n Fields: ['ID', 'Name', 'DisplayName', 'NameSuffix', 'Description', 'SchemaName', 'BaseTable', 'BaseView'],\n OrderBy: `${sortBy} ${sortDirection.toUpperCase()}`,\n ExtraFilter: filterParts.length > 0 ? filterParts.join(' AND ') : undefined\n });\n \n if (result?.Success && result?.Results) {\n setEntities(result.Results);\n \n // Extract unique schemas and tables for filter dropdowns\n const schemas = [...new Set(result.Results.map(e => e.SchemaName).filter(Boolean))];\n const tables = [...new Set(result.Results.map(e => e.BaseTable).filter(Boolean))];\n setUniqueSchemas(schemas);\n setUniqueTables(tables);\n } else {\n console.error('Failed to load entities:', result?.ErrorMessage);\n setEntities([]);\n }\n } catch (error) {\n console.error('Error loading entities:', error);\n setEntities([]);\n } finally {\n setLoading(false);\n }\n };\n \n loadEntities();\n }, [filters, sortBy, sortDirection, searchQuery, utilities.rv]);\n \n // Load entity details when selection changes\n useEffect(() => {\n const loadEntityDetails = async () => {\n if (!selectedEntityId) {\n setEntityFields([]);\n setEntityRelationships([]);\n return;\n }\n \n try {\n // Load fields\n const fieldsResult = await utilities.rv.RunView({\n EntityName: 'Entity Fields',\n Fields: ['Name', 'DisplayName', 'Type', 'Length', 'AllowsNull', 'IsPrimaryKey', 'IsUnique', 'Sequence'],\n OrderBy: 'Sequence ASC, Name ASC',\n ExtraFilter: `EntityID = '${selectedEntityId}'`\n });\n \n if (fieldsResult?.Success && fieldsResult?.Results) {\n setEntityFields(fieldsResult.Results);\n } else {\n setEntityFields([]);\n }\n \n // Load relationships\n const relationshipsResult = await utilities.rv.RunView({\n EntityName: 'Entity Relationships',\n Fields: ['RelatedEntity', 'Type', 'DisplayName', 'RelatedEntityJoinField', 'Sequence'],\n OrderBy: 'Sequence ASC, RelatedEntity ASC',\n ExtraFilter: `EntityID = '${selectedEntityId}'`\n });\n \n if (relationshipsResult?.Success && relationshipsResult?.Results) {\n setEntityRelationships(relationshipsResult.Results);\n } else {\n setEntityRelationships([]);\n }\n } catch (error) {\n console.error('Error loading entity details:', error);\n setEntityFields([]);\n setEntityRelationships([]);\n }\n };\n \n loadEntityDetails();\n }, [selectedEntityId, utilities.rv]);\n \n // Handle entity selection\n const handleSelectEntity = useCallback((entityId) => {\n setSelectedEntityId(entityId);\n setDetailsPanelOpen(true);\n \n // Save user preference\n onSaveUserSettings?.({\n ...savedUserSettings,\n selectedEntityId: entityId\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle view mode change\n const handleViewModeChange = useCallback((mode) => {\n setViewMode(mode);\n \n // Save preference\n onSaveUserSettings?.({\n ...savedUserSettings,\n viewMode: mode\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle filter changes\n const handleFilterChange = useCallback((newFilters) => {\n setFilters(newFilters);\n \n // Save filter preferences\n onSaveUserSettings?.({\n ...savedUserSettings,\n filters: newFilters\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle sort changes\n const handleSortChange = useCallback((newSortBy, newSortDirection) => {\n setSortBy(newSortBy);\n setSortDirection(newSortDirection);\n \n // Save sort preferences\n onSaveUserSettings?.({\n ...savedUserSettings,\n sortBy: newSortBy,\n sortDirection: newSortDirection\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle filter panel toggle\n const handleToggleFilter = useCallback(() => {\n const newCollapsed = !filterPanelCollapsed;\n setFilterPanelCollapsed(newCollapsed);\n \n // Save collapsed state\n onSaveUserSettings?.({\n ...savedUserSettings,\n filterPanelCollapsed: newCollapsed\n });\n }, [filterPanelCollapsed, savedUserSettings, onSaveUserSettings]);\n \n // Handle opening entity record (kept for backward compatibility with details panel)\n const handleOpenRecord = useCallback((entityName) => {\n console.log('Root handleOpenRecord called with entityName:', entityName);\n console.log('Callbacks object:', callbacks);\n if (callbacks?.OpenEntityRecord && entityName) {\n console.log('Calling OpenEntityRecord callback with:', 'Entities', entityName);\n // Open the Entities entity record for the selected entity\n callbacks.OpenEntityRecord('Entities', [{ FieldName: 'Name', Value: entityName }]);\n } else {\n console.error('OpenEntityRecord callback not available or entityName missing');\n }\n }, [callbacks]);\n \n // Handle closing details panel\n const handleCloseDetails = useCallback(() => {\n setDetailsPanelOpen(false);\n }, []);\n \n // Handle search\n const handleSearch = useCallback((query) => {\n setSearchQuery(query);\n }, []);\n \n // Get selected entity object\n const selectedEntity = entities.find(e => e.ID === selectedEntityId);\n \n // Helper function to get border radius value\n const getBorderRadius = (size) => {\n return typeof styles.borders.radius === 'object' ? styles.borders.radius[size] : styles.borders.radius;\n };\n \n // Loading state\n if (loading && entities.length === 0) {\n return (\n <div style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontSize: styles.typography.fontSize.lg,\n color: styles.colors.textSecondary\n }}>\n Loading entities...\n </div>\n );\n }\n \n return (\n <div style={{\n display: 'flex',\n height: '100vh',\n backgroundColor: styles.colors.background,\n overflow: 'hidden'\n }}>\n {/* Filter Panel */}\n {EntityFilter && (\n <EntityFilter\n filters={filters}\n onFilterChange={handleFilterChange}\n schemas={uniqueSchemas}\n tables={uniqueTables}\n isCollapsed={filterPanelCollapsed}\n onToggleCollapse={handleToggleFilter}\n savedUserSettings={savedUserSettings?.filterPanel}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n filterPanel: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n \n {/* Main Content Area */}\n <div style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden'\n }}>\n {/* Header */}\n <div style={{\n padding: styles.spacing.lg,\n borderBottom: `1px solid ${styles.colors.border}`,\n backgroundColor: styles.colors.surface\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: styles.spacing.md\n }}>\n <h1 style={{\n margin: 0,\n fontSize: styles.typography.fontSize.xxl || styles.typography.fontSize.xl,\n fontWeight: styles.typography.fontWeight?.bold || '700',\n color: styles.colors.text\n }}>\n Entity Browser\n </h1>\n \n {/* View Mode Toggle */}\n <div style={{\n display: 'flex',\n gap: styles.spacing.sm,\n alignItems: 'center'\n }}>\n <span style={{\n fontSize: styles.typography.fontSize.md,\n color: styles.colors.textSecondary\n }}>\n View:\n </span>\n <button\n onClick={() => handleViewModeChange('grid')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: viewMode === 'grid' ? styles.colors.primary : styles.colors.background,\n color: viewMode === 'grid' ? 'white' : styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Grid\n </button>\n <button\n onClick={() => handleViewModeChange('card')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: viewMode === 'card' ? styles.colors.primary : styles.colors.background,\n color: viewMode === 'card' ? 'white' : styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Cards\n </button>\n </div>\n </div>\n \n {/* Search Bar */}\n <div style={{\n display: 'flex',\n gap: styles.spacing.md\n }}>\n <input\n type=\"text\"\n placeholder=\"Search entities...\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n style={{\n flex: 1,\n padding: styles.spacing.md,\n fontSize: styles.typography.fontSize.md,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n backgroundColor: styles.colors.background\n }}\n />\n {searchQuery && (\n <button\n onClick={() => handleSearch('')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: styles.colors.surfaceHover || styles.colors.surface,\n color: styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Clear\n </button>\n )}\n </div>\n </div>\n \n {/* Entity List */}\n <div style={{\n flex: 1,\n overflow: 'auto',\n padding: styles.spacing.lg\n }}>\n {EntityList && (\n <EntityList\n entities={entities}\n viewMode={viewMode}\n selectedEntityId={selectedEntityId}\n onSelectEntity={handleSelectEntity}\n sortBy={sortBy}\n sortDirection={sortDirection}\n onSortChange={handleSortChange}\n savedUserSettings={savedUserSettings?.entityList}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n entityList: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n \n {/* Empty State */}\n {entities.length === 0 && !loading && (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: styles.spacing.xxl || styles.spacing.xl,\n color: styles.colors.textSecondary\n }}>\n <div style={{\n fontSize: styles.typography.fontSize.xl,\n marginBottom: styles.spacing.md\n }}>\n No entities found\n </div>\n <div style={{\n fontSize: styles.typography.fontSize.md\n }}>\n {searchQuery || Object.keys(filters).length > 0\n ? 'Try adjusting your filters or search query'\n : 'No entities are available'}\n </div>\n </div>\n )}\n </div>\n </div>\n \n {/* Details Panel */}\n {EntityDetails && (\n <EntityDetails\n entity={selectedEntity}\n fields={entityFields}\n relationships={entityRelationships}\n isOpen={detailsPanelOpen}\n onClose={handleCloseDetails}\n onOpenRecord={() => handleOpenRecord(selectedEntity?.Name)}\n savedUserSettings={savedUserSettings?.detailsPanel}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n detailsPanel: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n </div>\n );\n}"
|
|
1512
|
+
"code": "function EntityBrowser({ utilities, styles, components, callbacks, savedUserSettings, onSaveUserSettings }) {\n // Extract child components\n const { EntityList, EntityDetails, EntityFilter } = components;\n \n // Initialize state from saved settings where appropriate\n const [selectedEntityId, setSelectedEntityId] = useState(savedUserSettings?.selectedEntityId);\n const [viewMode, setViewMode] = useState(savedUserSettings?.viewMode || 'grid');\n const [filters, setFilters] = useState(savedUserSettings?.filters || {});\n const [sortBy, setSortBy] = useState(savedUserSettings?.sortBy || 'Name');\n const [sortDirection, setSortDirection] = useState(savedUserSettings?.sortDirection || 'asc');\n const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(savedUserSettings?.filterPanelCollapsed || false);\n \n // Runtime UI state (not persisted)\n const [entities, setEntities] = useState([]);\n const [entityFields, setEntityFields] = useState([]);\n const [entityRelationships, setEntityRelationships] = useState([]);\n const [loading, setLoading] = useState(true);\n const [detailsPanelOpen, setDetailsPanelOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [uniqueSchemas, setUniqueSchemas] = useState([]);\n const [uniqueTables, setUniqueTables] = useState([]);\n \n // Load entities on mount and when filters/sort change\n useEffect(() => {\n const loadEntities = async () => {\n setLoading(true);\n try {\n // Build filter string\n let filterParts = [];\n if (filters.schema) {\n filterParts.push(`SchemaName = '${filters.schema}'`);\n }\n if (filters.table) {\n filterParts.push(`BaseTable = '${filters.table}'`);\n }\n if (searchQuery) {\n filterParts.push(`(Name LIKE '%${searchQuery}%' OR DisplayName LIKE '%${searchQuery}%' OR Description LIKE '%${searchQuery}%')`);\n }\n \n const result = await utilities.rv.RunView({\n EntityName: 'MJ: Entities',\n Fields: ['ID', 'Name', 'DisplayName', 'NameSuffix', 'Description', 'SchemaName', 'BaseTable', 'BaseView'],\n OrderBy: `${sortBy} ${sortDirection.toUpperCase()}`,\n ExtraFilter: filterParts.length > 0 ? filterParts.join(' AND ') : undefined\n });\n \n if (result?.Success && result?.Results) {\n setEntities(result.Results);\n \n // Extract unique schemas and tables for filter dropdowns\n const schemas = [...new Set(result.Results.map(e => e.SchemaName).filter(Boolean))];\n const tables = [...new Set(result.Results.map(e => e.BaseTable).filter(Boolean))];\n setUniqueSchemas(schemas);\n setUniqueTables(tables);\n } else {\n console.error('Failed to load entities:', result?.ErrorMessage);\n setEntities([]);\n }\n } catch (error) {\n console.error('Error loading entities:', error);\n setEntities([]);\n } finally {\n setLoading(false);\n }\n };\n \n loadEntities();\n }, [filters, sortBy, sortDirection, searchQuery, utilities.rv]);\n \n // Load entity details when selection changes\n useEffect(() => {\n const loadEntityDetails = async () => {\n if (!selectedEntityId) {\n setEntityFields([]);\n setEntityRelationships([]);\n return;\n }\n \n try {\n // Load fields\n const fieldsResult = await utilities.rv.RunView({\n EntityName: 'MJ: Entity Fields',\n Fields: ['Name', 'DisplayName', 'Type', 'Length', 'AllowsNull', 'IsPrimaryKey', 'IsUnique', 'Sequence'],\n OrderBy: 'Sequence ASC, Name ASC',\n ExtraFilter: `EntityID = '${selectedEntityId}'`\n });\n \n if (fieldsResult?.Success && fieldsResult?.Results) {\n setEntityFields(fieldsResult.Results);\n } else {\n setEntityFields([]);\n }\n \n // Load relationships\n const relationshipsResult = await utilities.rv.RunView({\n EntityName: 'MJ: Entity Relationships',\n Fields: ['RelatedEntity', 'Type', 'DisplayName', 'RelatedEntityJoinField', 'Sequence'],\n OrderBy: 'Sequence ASC, RelatedEntity ASC',\n ExtraFilter: `EntityID = '${selectedEntityId}'`\n });\n \n if (relationshipsResult?.Success && relationshipsResult?.Results) {\n setEntityRelationships(relationshipsResult.Results);\n } else {\n setEntityRelationships([]);\n }\n } catch (error) {\n console.error('Error loading entity details:', error);\n setEntityFields([]);\n setEntityRelationships([]);\n }\n };\n \n loadEntityDetails();\n }, [selectedEntityId, utilities.rv]);\n \n // Handle entity selection\n const handleSelectEntity = useCallback((entityId) => {\n setSelectedEntityId(entityId);\n setDetailsPanelOpen(true);\n \n // Save user preference\n onSaveUserSettings?.({\n ...savedUserSettings,\n selectedEntityId: entityId\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle view mode change\n const handleViewModeChange = useCallback((mode) => {\n setViewMode(mode);\n \n // Save preference\n onSaveUserSettings?.({\n ...savedUserSettings,\n viewMode: mode\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle filter changes\n const handleFilterChange = useCallback((newFilters) => {\n setFilters(newFilters);\n \n // Save filter preferences\n onSaveUserSettings?.({\n ...savedUserSettings,\n filters: newFilters\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle sort changes\n const handleSortChange = useCallback((newSortBy, newSortDirection) => {\n setSortBy(newSortBy);\n setSortDirection(newSortDirection);\n \n // Save sort preferences\n onSaveUserSettings?.({\n ...savedUserSettings,\n sortBy: newSortBy,\n sortDirection: newSortDirection\n });\n }, [savedUserSettings, onSaveUserSettings]);\n \n // Handle filter panel toggle\n const handleToggleFilter = useCallback(() => {\n const newCollapsed = !filterPanelCollapsed;\n setFilterPanelCollapsed(newCollapsed);\n \n // Save collapsed state\n onSaveUserSettings?.({\n ...savedUserSettings,\n filterPanelCollapsed: newCollapsed\n });\n }, [filterPanelCollapsed, savedUserSettings, onSaveUserSettings]);\n \n // Handle opening entity record (kept for backward compatibility with details panel)\n const handleOpenRecord = useCallback((entityName) => {\n console.log('Root handleOpenRecord called with entityName:', entityName);\n console.log('Callbacks object:', callbacks);\n if (callbacks?.OpenEntityRecord && entityName) {\n console.log('Calling OpenEntityRecord callback with:', 'MJ: Entities', entityName);\n // Open the Entities entity record for the selected entity\n callbacks.OpenEntityRecord('MJ: Entities', [{ FieldName: 'Name', Value: entityName }]);\n } else {\n console.error('OpenEntityRecord callback not available or entityName missing');\n }\n }, [callbacks]);\n \n // Handle closing details panel\n const handleCloseDetails = useCallback(() => {\n setDetailsPanelOpen(false);\n }, []);\n \n // Handle search\n const handleSearch = useCallback((query) => {\n setSearchQuery(query);\n }, []);\n \n // Get selected entity object\n const selectedEntity = entities.find(e => e.ID === selectedEntityId);\n \n // Helper function to get border radius value\n const getBorderRadius = (size) => {\n return typeof styles.borders.radius === 'object' ? styles.borders.radius[size] : styles.borders.radius;\n };\n \n // Loading state\n if (loading && entities.length === 0) {\n return (\n <div style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontSize: styles.typography.fontSize.lg,\n color: styles.colors.textSecondary\n }}>\n Loading entities...\n </div>\n );\n }\n \n return (\n <div style={{\n display: 'flex',\n height: '100vh',\n backgroundColor: styles.colors.background,\n overflow: 'hidden'\n }}>\n {/* Filter Panel */}\n {EntityFilter && (\n <EntityFilter\n filters={filters}\n onFilterChange={handleFilterChange}\n schemas={uniqueSchemas}\n tables={uniqueTables}\n isCollapsed={filterPanelCollapsed}\n onToggleCollapse={handleToggleFilter}\n savedUserSettings={savedUserSettings?.filterPanel}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n filterPanel: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n \n {/* Main Content Area */}\n <div style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden'\n }}>\n {/* Header */}\n <div style={{\n padding: styles.spacing.lg,\n borderBottom: `1px solid ${styles.colors.border}`,\n backgroundColor: styles.colors.surface\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: styles.spacing.md\n }}>\n <h1 style={{\n margin: 0,\n fontSize: styles.typography.fontSize.xxl || styles.typography.fontSize.xl,\n fontWeight: styles.typography.fontWeight?.bold || '700',\n color: styles.colors.text\n }}>\n Entity Browser\n </h1>\n \n {/* View Mode Toggle */}\n <div style={{\n display: 'flex',\n gap: styles.spacing.sm,\n alignItems: 'center'\n }}>\n <span style={{\n fontSize: styles.typography.fontSize.md,\n color: styles.colors.textSecondary\n }}>\n View:\n </span>\n <button\n onClick={() => handleViewModeChange('grid')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: viewMode === 'grid' ? styles.colors.primary : styles.colors.background,\n color: viewMode === 'grid' ? 'white' : styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Grid\n </button>\n <button\n onClick={() => handleViewModeChange('card')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: viewMode === 'card' ? styles.colors.primary : styles.colors.background,\n color: viewMode === 'card' ? 'white' : styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Cards\n </button>\n </div>\n </div>\n \n {/* Search Bar */}\n <div style={{\n display: 'flex',\n gap: styles.spacing.md\n }}>\n <input\n type=\"text\"\n placeholder=\"Search entities...\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n style={{\n flex: 1,\n padding: styles.spacing.md,\n fontSize: styles.typography.fontSize.md,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n backgroundColor: styles.colors.background\n }}\n />\n {searchQuery && (\n <button\n onClick={() => handleSearch('')}\n style={{\n padding: `${styles.spacing.sm} ${styles.spacing.md}`,\n backgroundColor: styles.colors.surfaceHover || styles.colors.surface,\n color: styles.colors.text,\n border: `1px solid ${styles.colors.border}`,\n borderRadius: getBorderRadius('sm'),\n cursor: 'pointer',\n fontSize: styles.typography.fontSize.md\n }}\n >\n Clear\n </button>\n )}\n </div>\n </div>\n \n {/* Entity List */}\n <div style={{\n flex: 1,\n overflow: 'auto',\n padding: styles.spacing.lg\n }}>\n {EntityList && (\n <EntityList\n entities={entities}\n viewMode={viewMode}\n selectedEntityId={selectedEntityId}\n onSelectEntity={handleSelectEntity}\n sortBy={sortBy}\n sortDirection={sortDirection}\n onSortChange={handleSortChange}\n savedUserSettings={savedUserSettings?.entityList}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n entityList: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n \n {/* Empty State */}\n {entities.length === 0 && !loading && (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: styles.spacing.xxl || styles.spacing.xl,\n color: styles.colors.textSecondary\n }}>\n <div style={{\n fontSize: styles.typography.fontSize.xl,\n marginBottom: styles.spacing.md\n }}>\n No entities found\n </div>\n <div style={{\n fontSize: styles.typography.fontSize.md\n }}>\n {searchQuery || Object.keys(filters).length > 0\n ? 'Try adjusting your filters or search query'\n : 'No entities are available'}\n </div>\n </div>\n )}\n </div>\n </div>\n \n {/* Details Panel */}\n {EntityDetails && (\n <EntityDetails\n entity={selectedEntity}\n fields={entityFields}\n relationships={entityRelationships}\n isOpen={detailsPanelOpen}\n onClose={handleCloseDetails}\n onOpenRecord={() => handleOpenRecord(selectedEntity?.Name)}\n savedUserSettings={savedUserSettings?.detailsPanel}\n onSaveUserSettings={(settings) => onSaveUserSettings?.({\n ...savedUserSettings,\n detailsPanel: settings\n })}\n utilities={utilities}\n styles={styles}\n components={components}\n callbacks={callbacks}\n />\n )}\n </div>\n );\n}"
|
|
1513
1513
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { AuthorizationError } from 'type-graphql';
|
|
8
8
|
import { GetAPIKeyEngine, AuthorizationResult, AuthorizationRequest } from '@memberjunction/api-keys';
|
|
9
9
|
import { UserInfo, RunView } from '@memberjunction/core';
|
|
10
|
-
import {
|
|
10
|
+
import { MJAPIKeyEntity, MJAPIApplicationEntity } from '@memberjunction/core-entities';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Application names used by the API Key authorization system
|
|
@@ -94,7 +94,7 @@ export async function CheckAPIKeyScope(
|
|
|
94
94
|
|
|
95
95
|
// Get the API key to find the user ID
|
|
96
96
|
const rv = new RunView();
|
|
97
|
-
const keyResult = await rv.RunView<
|
|
97
|
+
const keyResult = await rv.RunView<MJAPIKeyEntity>({
|
|
98
98
|
EntityName: 'MJ: API Keys',
|
|
99
99
|
ExtraFilter: `ID='${apiKeyId}'`,
|
|
100
100
|
ResultType: 'entity_object'
|
|
@@ -115,7 +115,7 @@ export async function CheckAPIKeyScope(
|
|
|
115
115
|
const apiKey = keyResult.Results[0];
|
|
116
116
|
|
|
117
117
|
// Get the application by name
|
|
118
|
-
const appResult = await rv.RunView<
|
|
118
|
+
const appResult = await rv.RunView<MJAPIApplicationEntity>({
|
|
119
119
|
EntityName: 'MJ: API Applications',
|
|
120
120
|
ExtraFilter: `Name='${applicationName}'`,
|
|
121
121
|
ResultType: 'entity_object'
|
|
@@ -222,7 +222,7 @@ export async function CheckAPIKeyScopeAndLog(
|
|
|
222
222
|
const rv = new RunView();
|
|
223
223
|
|
|
224
224
|
// Get the API key
|
|
225
|
-
const keyResult = await rv.RunView<
|
|
225
|
+
const keyResult = await rv.RunView<MJAPIKeyEntity>({
|
|
226
226
|
EntityName: 'MJ: API Keys',
|
|
227
227
|
ExtraFilter: `ID='${apiKeyId}'`,
|
|
228
228
|
ResultType: 'entity_object'
|
|
@@ -243,7 +243,7 @@ export async function CheckAPIKeyScopeAndLog(
|
|
|
243
243
|
const apiKey = keyResult.Results[0];
|
|
244
244
|
|
|
245
245
|
// Get the application
|
|
246
|
-
const appResult = await rv.RunView<
|
|
246
|
+
const appResult = await rv.RunView<MJAPIApplicationEntity>({
|
|
247
247
|
EntityName: 'MJ: API Applications',
|
|
248
248
|
ExtraFilter: `Name='${applicationName}'`,
|
|
249
249
|
ResultType: 'entity_object'
|
|
@@ -3,7 +3,7 @@ import { Metadata, RunView, LogError, EntitySaveOptions } from '@memberjunction/
|
|
|
3
3
|
import { NewUserBase } from './newUsers.js';
|
|
4
4
|
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
5
5
|
import { configInfo } from '../config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { MJUserEntity } from '@memberjunction/core-entities';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* This example class subclasses the @NewUserBase class and overrides the createNewUser method to create a new person record and then call the base class to create the user record. In this example there is an entity
|
|
@@ -14,7 +14,7 @@ import { UserEntity } from '@memberjunction/core-entities';
|
|
|
14
14
|
// so that your class is actually used.
|
|
15
15
|
//@RegisterClass(NewUserBase, undefined, 1) /*by putting 1 into the priority setting, MJGlobal ClassFactory will use this instead of the base class as that registration had no priority*/
|
|
16
16
|
export class ExampleNewUserSubClass extends NewUserBase {
|
|
17
|
-
public override async createNewUser(firstName: string, lastName: string, email: string, linkedRecordType: string = 'None', linkedEntityId?: string, linkedEntityRecordId?: string): Promise<
|
|
17
|
+
public override async createNewUser(firstName: string, lastName: string, email: string, linkedRecordType: string = 'None', linkedEntityId?: string, linkedEntityRecordId?: string): Promise<MJUserEntity | null> {
|
|
18
18
|
try {
|
|
19
19
|
const md = new Metadata();
|
|
20
20
|
|
package/src/auth/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import sql from 'mssql';
|
|
|
5
5
|
import { Metadata, RoleInfo, UserInfo } from '@memberjunction/core';
|
|
6
6
|
import { NewUserBase } from './newUsers.js';
|
|
7
7
|
import { MJGlobal } from '@memberjunction/global';
|
|
8
|
-
import {
|
|
8
|
+
import { MJUserEntity, MJUserEntityType } from '@memberjunction/core-entities';
|
|
9
9
|
import { AuthProviderFactory } from './AuthProviderFactory.js';
|
|
10
10
|
import { initializeAuthProviders } from './initializeProviders.js';
|
|
11
11
|
|
|
@@ -227,13 +227,13 @@ export const verifyUserRecord = async (
|
|
|
227
227
|
// we have a domain from the request that matches one of the domains provided by the configuration, so we will create a new user
|
|
228
228
|
console.warn(`User ${email} not found in cache. Attempting to create a new user...`);
|
|
229
229
|
const newUserCreator: NewUserBase = MJGlobal.Instance.ClassFactory.CreateInstance<NewUserBase>(NewUserBase); // this will create the object that handles creating the new user for us
|
|
230
|
-
const newUser:
|
|
230
|
+
const newUser: MJUserEntity | null = await newUserCreator.createNewUser(firstName, lastName, email);
|
|
231
231
|
if (newUser) {
|
|
232
232
|
// new user worked! we already have the stuff we need for the cache, so no need to go to the DB now, just create a new UserInfo object and use the return value from the createNewUser method
|
|
233
233
|
// to init it, including passing in the role list for the user.
|
|
234
234
|
const md: Metadata = new Metadata();
|
|
235
235
|
|
|
236
|
-
const initData:
|
|
236
|
+
const initData: MJUserEntityType & { UserRoles: { UserID: string; RoleName: string; RoleID: string }[] } = newUser.GetAll();
|
|
237
237
|
|
|
238
238
|
initData.UserRoles = configInfo.userHandling.newUserRoles.map((role) => {
|
|
239
239
|
const roleInfo: RoleInfo | undefined = md.Roles.find((r) => r.Name === role);
|
package/src/auth/newUsers.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { ApplicationInfo, EntitySaveOptions, LogError, LogStatus, Metadata, RunV
|
|
|
2
2
|
import { RegisterClass } from "@memberjunction/global";
|
|
3
3
|
import { UserCache } from "@memberjunction/sqlserver-dataprovider";
|
|
4
4
|
import { configInfo } from "../config.js";
|
|
5
|
-
import {
|
|
5
|
+
import { MJUserEntity, MJUserRoleEntity, MJUserApplicationEntity, MJUserApplicationEntityEntity, MJApplicationEntityType, MJApplicationEntityEntityType } from "@memberjunction/core-entities";
|
|
6
6
|
|
|
7
7
|
export class NewUserBase {
|
|
8
|
-
public async createNewUser(firstName: string, lastName: string, email: string, linkedRecordType: string = 'None', linkedEntityId?: string, linkedEntityRecordId?: string): Promise<
|
|
8
|
+
public async createNewUser(firstName: string, lastName: string, email: string, linkedRecordType: string = 'None', linkedEntityId?: string, linkedEntityRecordId?: string): Promise<MJUserEntity | null> {
|
|
9
9
|
try {
|
|
10
10
|
let contextUser: UserInfo | null = null;
|
|
11
11
|
|
|
@@ -25,7 +25,7 @@ export class NewUserBase {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const md: Metadata = new Metadata();
|
|
28
|
-
const user = await md.GetEntityObject<
|
|
28
|
+
const user = await md.GetEntityObject<MJUserEntity>('MJ: Users', contextUser) // To-Do - change this to be a different defined user for the user creation process
|
|
29
29
|
user.NewRecord();
|
|
30
30
|
user.Name = email;
|
|
31
31
|
user.IsActive = true;
|
|
@@ -53,7 +53,7 @@ export class NewUserBase {
|
|
|
53
53
|
// user created, now create however many roles we need to create for this user based on the config settings
|
|
54
54
|
LogStatus(`User ${user.Email} created, assigning roles`);
|
|
55
55
|
for (const role of configInfo.userHandling.newUserRoles) {
|
|
56
|
-
const userRoleEntity:
|
|
56
|
+
const userRoleEntity: MJUserRoleEntity = await md.GetEntityObject<MJUserRoleEntity>('MJ: User Roles', contextUser);
|
|
57
57
|
userRoleEntity.NewRecord();
|
|
58
58
|
userRoleEntity.UserID = user.ID;
|
|
59
59
|
const userRole = md.Roles.find(r => r.Name === role);
|
|
@@ -106,7 +106,7 @@ export class NewUserBase {
|
|
|
106
106
|
|
|
107
107
|
// Create UserApplication records for each application
|
|
108
108
|
for (const [appIndex, application] of applicationsToCreate.entries()) {
|
|
109
|
-
const userApplication:
|
|
109
|
+
const userApplication: MJUserApplicationEntity = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications', contextUser);
|
|
110
110
|
userApplication.NewRecord();
|
|
111
111
|
userApplication.UserID = user.ID;
|
|
112
112
|
userApplication.ApplicationID = application.ID;
|
|
@@ -117,10 +117,10 @@ export class NewUserBase {
|
|
|
117
117
|
if(userApplicationSaveResult){
|
|
118
118
|
LogStatus(`Created User Application ${application.Name} for new user ${user.Name}`);
|
|
119
119
|
|
|
120
|
-
//now create a
|
|
120
|
+
//now create a MJUserApplicationEntity records for each entity in the application
|
|
121
121
|
const rv: RunView = new RunView();
|
|
122
|
-
const rvResult: RunViewResult<
|
|
123
|
-
EntityName: 'Application Entities',
|
|
122
|
+
const rvResult: RunViewResult<MJApplicationEntityEntityType> = await rv.RunView({
|
|
123
|
+
EntityName: 'MJ: Application Entities',
|
|
124
124
|
ExtraFilter: `ApplicationID = '${application.ID}' and DefaultForNewUser = 1`,
|
|
125
125
|
}, contextUser);
|
|
126
126
|
|
|
@@ -132,7 +132,7 @@ export class NewUserBase {
|
|
|
132
132
|
LogStatus(`Creating ${rvResult.Results.length} User Application Entities for User Application ${application.Name} for new user ${user.Name}`);
|
|
133
133
|
|
|
134
134
|
for(const [index, appEntity] of rvResult.Results.entries()){
|
|
135
|
-
const userAppEntity:
|
|
135
|
+
const userAppEntity: MJUserApplicationEntityEntity = await md.GetEntityObject<MJUserApplicationEntityEntity>('MJ: User Application Entities', contextUser);
|
|
136
136
|
userAppEntity.NewRecord();
|
|
137
137
|
userAppEntity.UserApplicationID = userApplication.ID;
|
|
138
138
|
userAppEntity.EntityID = appEntity.EntityID;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RegisterClass } from '@memberjunction/global';
|
|
2
2
|
import { BaseEntity, EntityDeleteOptions, EntitySaveOptions } from '@memberjunction/core';
|
|
3
|
-
import {
|
|
3
|
+
import { MJEntityPermissionEntity } from '@memberjunction/core-entities';
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
import { ___codeGenAPIPort, ___codeGenAPISubmissionDelay, ___codeGenAPIURL } from '../config.js';
|
|
6
6
|
|
|
@@ -11,8 +11,8 @@ import { ___codeGenAPIPort, ___codeGenAPISubmissionDelay, ___codeGenAPIURL } fro
|
|
|
11
11
|
* This class is within the memberjunction/server package because it is closely coupled to other aspects of what
|
|
12
12
|
* happens in the server. That's why it is not in the core-entities-server package.
|
|
13
13
|
*/
|
|
14
|
-
@RegisterClass(BaseEntity, 'Entity Permissions')
|
|
15
|
-
export class EntityPermissionsEntity_Server extends
|
|
14
|
+
@RegisterClass(BaseEntity, 'MJ: Entity Permissions')
|
|
15
|
+
export class EntityPermissionsEntity_Server extends MJEntityPermissionEntity {
|
|
16
16
|
protected static _entityIDQueue: string[] = [];
|
|
17
17
|
protected static _lastModifiedTime: Date | null = null;
|
|
18
18
|
protected static _submissionTimer: NodeJS.Timeout | null = null;
|