Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] [WRS-2078] Implement data connector authorization #218

Merged
merged 17 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 1 addition & 48 deletions .github/workflows/bundle-size-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Get bundle size for current PR
id: get_current_bundle_size
run: |
current_bundle_size=$(du -sb dist/ | cut -f1)
current_bundle_size=$(du -sb dist/bundle.js | cut -f1)
echo "Current PR size: $current_bundle_size bytes"
echo "::set-output name=current_bundle_size::$current_bundle_size"

Expand Down Expand Up @@ -61,50 +61,3 @@ jobs:
else
echo "Bundle size is within the acceptable range."
fi

update-bundle-size:
needs: check-bundle-size
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.merged }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]
steps:
- uses: FranzDiebold/github-env-vars-action@v2
- uses: actions/checkout@v3
with:
token: ${{ secrets.PACKAGE_SECRET }}
- name: Use Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://npm.pkg.github.com'
scope: '@chili-publish'
- name: install dependencies
run: |
yarn install
env:
NODE_AUTH_TOKEN: ${{ secrets.PACKAGE_SECRET }}

- name: Build project
run: yarn build

- name: Get bundle size after merge
id: get_new_bundle_size
run: |
new_bundle_size=$(du -sb dist/ | cut -f1)
echo "New size after merge: $new_bundle_size"
echo "::set-output name=new_bundle_size::$new_bundle_size"

- name: Update bundle-size.json
run: |
new_bundle_size=${{ steps.get_new_bundle_size.outputs.new_bundle_size }}
echo "{ \"bundle_size\": $new_bundle_size }" > bundle-size.json

- name: Commit and push updated bundle size
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email user.email 'github-actions[bot]@users.noreply.github.com'
git add bundle-size.json
git commit -m "Update bundle size"
git push origin main --follow-tags
48 changes: 48 additions & 0 deletions .github/workflows/bundle-size-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Bundle Size Update
on:
push:
branches:
- main
jobs:
update-bundle-size:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]
steps:
- uses: FranzDiebold/github-env-vars-action@v2
- uses: actions/checkout@v3
with:
token: ${{ secrets.PACKAGE_SECRET }}
- name: Use Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://npm.pkg.github.com'
scope: '@chili-publish'
- name: install dependencies
run: |
yarn install
env:
NODE_AUTH_TOKEN: ${{ secrets.PACKAGE_SECRET }}

- name: Build project
run: yarn build

- name: Get bundle size
id: get_new_bundle_size
run: |
new_bundle_size=$(du -sb dist/bundle.js | cut -f1)
echo "New size: $new_bundle_size"
echo "::set-output name=new_bundle_size::$new_bundle_size"
- name: Update bundle-size.json
run: |
new_bundle_size=${{ steps.get_new_bundle_size.outputs.new_bundle_size }}
echo "{ \"bundle_size\": $new_bundle_size }" > bundle-size.json
- name: Commit and push updated bundle size
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email user.email 'github-actions[bot]@users.noreply.github.com'
git add bundle-size.json
git commit -m "Update bundle size"
git push origin main --follow-tags
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"dependencies": {
"@chili-publish/grafx-shared-components": "^0.85.0",
"@chili-publish/studio-sdk": "^1.15.0-rc.6",
"@chili-publish/studio-sdk": "^1.16.0-rc.12",
"axios": "^1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SubscriberContextProvider } from './contexts/Subscriber';
import MainContent from './MainContent';
import { ProjectConfig } from './types/types';
import { Subscriber } from './utils/subscriber';
import FeatureFlagProvider from './contexts/FeatureFlagProvider';

function App({ projectConfig }: { projectConfig: ProjectConfig }) {
const [authToken, setAuthToken] = useState(projectConfig.onAuthenticationRequested());
Expand Down Expand Up @@ -81,7 +82,9 @@ function App({ projectConfig }: { projectConfig: ProjectConfig }) {
<SubscriberContextProvider subscriber={eventSubscriber}>
<UiThemeProvider theme="platform" mode={uiThemeMode}>
<NotificationManagerProvider>
<MainContent authToken={authToken} updateToken={setAuthToken} projectConfig={projectConfig} />
<FeatureFlagProvider featureFlags={projectConfig.featureFlags}>
<MainContent authToken={authToken} updateToken={setAuthToken} projectConfig={projectConfig} />
</FeatureFlagProvider>
</NotificationManagerProvider>
</UiThemeProvider>
</SubscriberContextProvider>
Expand Down
36 changes: 21 additions & 15 deletions src/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ function MainContent({ projectConfig, authToken, updateToken: setAuthToken }: Ma
);

const {
result: connectorAuthResult,
pendingAuthentications,
authResults,
process: connectorAuthenticationProcess,
createProcess: createAuthenticationProcess,
connectorName,
} = useConnectorAuthentication();

useConnectorAuthenticationResult(connectorAuthResult);
useConnectorAuthenticationResult(authResults);

useEffect(() => {
projectConfig
Expand Down Expand Up @@ -145,11 +145,15 @@ function MainContent({ projectConfig, authToken, updateToken: setAuthToken }: Ma
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, remoteConnectorId] = request.headerValue.split(';').map((i) => i.trim());
const connector = await window.StudioUISDK.next.connector.getById(request.connectorId);
const result = await createAuthenticationProcess(async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const res = await projectConfig.onConnectorAuthenticationRequested!(remoteConnectorId);
return res;
}, connector.parsedData?.name ?? '');
const result = await createAuthenticationProcess(
async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const res = await projectConfig.onConnectorAuthenticationRequested!(remoteConnectorId);
return res;
},
connector.parsedData?.name ?? '',
remoteConnectorId,
);
return result;
}
} catch (error) {
Expand Down Expand Up @@ -345,13 +349,15 @@ function MainContent({ projectConfig, authToken, updateToken: setAuthToken }: Ma
) : null}
</CanvasContainer>
</MainContentContainer>
{connectorAuthenticationProcess && (
<ConnectorAuthenticationModal
name={connectorName}
onConfirm={() => connectorAuthenticationProcess.start()}
onCancel={() => connectorAuthenticationProcess.cancel()}
/>
)}
{pendingAuthentications.length &&
pendingAuthentications.map((authFlow) => (
<ConnectorAuthenticationModal
key={authFlow.connectorId}
name={authFlow.connectorName}
onConfirm={() => connectorAuthenticationProcess(authFlow.connectorId)?.start()}
onCancel={() => connectorAuthenticationProcess(authFlow.connectorId)?.cancel()}
/>
))}
</div>
</VariablePanelContextProvider>
</UiConfigContextProvider>
Expand Down
7 changes: 7 additions & 0 deletions src/components/connector-authentication/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ConnectorAuthenticationResult } from 'src/types/ConnectorAuthenticationResult';

export type ConnectorAuthResult = {
result: ConnectorAuthenticationResult | null;
connectorName: string;
connectorId: string;
};
Original file line number Diff line number Diff line change
@@ -1,81 +1,101 @@
import { RefreshedAuthCredendentials } from '@chili-publish/studio-sdk';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useState } from 'react';
import { ConnectorAuthenticationResult } from '../../types/ConnectorAuthenticationResult';
import { ConnectorAuthResult } from './types';

interface Executor {
handler: () => Promise<ConnectorAuthenticationResult>;
}

export const useConnectorAuthentication = () => {
const [authenticationResolvers, setAuthenticationResolvers] = useState<Omit<
// eslint-disable-next-line no-undef
PromiseWithResolvers<RefreshedAuthCredendentials | null>,
'promise'
> | null>(null);
const [executor, setExecutor] = useState<Executor | null>(null);
const [connectorName, setConnectorName] = useState<string>('');
const [result, setResult] = useState<ConnectorAuthenticationResult | null>(null);
export type ConnectorPendingAuthentication = {
connectorId: string;
connectorName: string;
authenticationResolvers: Omit<PromiseWithResolvers<RefreshedAuthCredendentials | null>, 'promise'> | null;
executor: Executor | null;
};

const resetProcess = useCallback(() => {
setAuthenticationResolvers(null);
setExecutor(null);
export const useConnectorAuthentication = () => {
const [pendingAuthentications, setPendingAuthentications] = useState<ConnectorPendingAuthentication[]>([]);
const [authResults, setAuthResults] = useState<ConnectorAuthResult[]>([]);
const resetProcess = useCallback((id: string) => {
setPendingAuthentications((prev) => prev.filter((item) => item.connectorId !== id));
}, []);

const process = useMemo(() => {
if (authenticationResolvers && executor) {
return {
__resolvers: authenticationResolvers,
async start() {
setResult(null);
try {
const executorResult = await executor.handler().then((res) => {
setResult(res);
if (res.type === 'authentified') {
return new RefreshedAuthCredendentials();
}
// eslint-disable-next-line no-console
console.warn(`There is a "${res.type}" issue with authentifying of the connector`);
if (res.type === 'error') {
const process = useCallback(
(id: string) => {
const authenticationProcess = pendingAuthentications.find((item) => item.connectorId === id);
if (!authenticationProcess) return null;

if (authenticationProcess.authenticationResolvers && authenticationProcess.executor) {
return {
__resolvers: authenticationProcess.authenticationResolvers,
async start() {
try {
const executorResult = await authenticationProcess.executor?.handler().then((res) => {
setAuthResults((prev) => [
...prev,
{
result: res,
connectorName: authenticationProcess.connectorName,
alexandraFlavia9 marked this conversation as resolved.
Show resolved Hide resolved
connectorId: authenticationProcess.connectorId,
},
]);

if (res.type === 'authentified') {
return new RefreshedAuthCredendentials();
}
// eslint-disable-next-line no-console
console.error(res.error);
}
console.warn(`There is a "${res.type}" issue with authentifying of the connector`);
if (res.type === 'error') {
// eslint-disable-next-line no-console
console.error(res.error);
}

return null;
});
this.__resolvers.resolve(executorResult);
} catch (error) {
this.__resolvers.reject(error);
} finally {
resetProcess();
}
},
async cancel() {
this.__resolvers.resolve(null);
resetProcess();
},
};
}
return null;
}, [authenticationResolvers, executor, resetProcess]);
return null;
});
this.__resolvers.resolve(executorResult || null);
} catch (error) {
this.__resolvers.reject(error);
} finally {
resetProcess(id);
}
},
async cancel() {
this.__resolvers.resolve(null);
resetProcess(id);
},
};
}
return null;
},
[pendingAuthentications, resetProcess],
);

const createProcess = async (authorizationExecutor: Executor['handler'], name: string) => {
const createProcess = async (authorizationExecutor: Executor['handler'], name: string, id: string) => {
const authenticationAwaiter = Promise.withResolvers<RefreshedAuthCredendentials | null>();
setExecutor({
handler: authorizationExecutor,
});
setAuthenticationResolvers({
resolve: authenticationAwaiter.resolve,
reject: authenticationAwaiter.reject,
});
setConnectorName(name);

setPendingAuthentications((prev) => [
...prev,
{
executor: {
handler: authorizationExecutor,
},
authenticationResolvers: {
resolve: authenticationAwaiter.resolve,
reject: authenticationAwaiter.reject,
},
connectorName: name,
connectorId: id,
},
]);
const promiseResult = await authenticationAwaiter.promise;
return promiseResult;
};

return {
authResults,
pendingAuthentications,
createProcess,
process,
connectorName,
result,
};
};
Loading
Loading