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

ODC-7275: Implement invoke serverless functions #12755

Merged
merged 1 commit into from Jun 16, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -632,6 +632,7 @@ export type CodeEditorProps = {
options?: object;
minHeight?: string | number;
showShortcuts?: boolean;
showMiniMap?: boolean;
toolbarLinks?: React.ReactNodeArray;
onChange?: (newValue, event) => void;
onSave?: () => void;
Expand Down
Expand Up @@ -13,6 +13,7 @@ const CodeEditor = React.forwardRef<MonacoEditor, CodeEditorProps>((props, ref)
value,
options = defaultEditorOptions,
showShortcuts,
showMiniMap,
toolbarLinks,
minHeight,
onChange,
Expand All @@ -23,15 +24,34 @@ const CodeEditor = React.forwardRef<MonacoEditor, CodeEditorProps>((props, ref)
const [usesValue] = React.useState<boolean>(value !== undefined);
const editorDidMount = React.useCallback(
(editor, monaco) => {
const currentLanguage = editor.getModel().getModeId();
editor.layout();
editor.focus();
registerYAMLinMonaco(editor, monaco, usesValue);
switch (currentLanguage) {
case 'yaml':
registerYAMLinMonaco(editor, monaco, usesValue);
break;
case 'json':
editor.getAction('editor.action.formatDocument').run();
break;
default:
break;
}
monaco.editor.getModels()[0].updateOptions({ tabSize: 2 });
onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, onSave); // eslint-disable-line no-bitwise
},
[onSave, usesValue],
);

const editorOptions = React.useMemo(() => {
return {
...options,
minimap: {
enabled: showMiniMap,
},
};
}, [options, showMiniMap]);

return (
<>
<CodeEditorToolbar showShortcuts={showShortcuts} toolbarLinks={toolbarLinks} />
Expand All @@ -46,7 +66,7 @@ const CodeEditor = React.forwardRef<MonacoEditor, CodeEditorProps>((props, ref)
height={contentRect.bounds.height}
width={contentRect.bounds.width}
value={value}
options={options}
options={editorOptions}
editorDidMount={editorDidMount}
onChange={onChange}
/>
Expand Down
Expand Up @@ -32,6 +32,7 @@ const CodeEditorField: React.FC<CodeEditorFieldProps> = ({
schema,
showSamples,
showShortcuts,
showMiniMap,
minHeight,
onSave,
language,
Expand Down Expand Up @@ -89,6 +90,7 @@ const CodeEditorField: React.FC<CodeEditorFieldProps> = ({
onChange={(yaml: string) => setFieldValue(name, yaml)}
onSave={onSave}
showShortcuts={showShortcuts}
showMiniMap={showMiniMap}
language={language}
toolbarLinks={
!sidebarOpen &&
Expand Down
Expand Up @@ -119,6 +119,7 @@ export interface CodeEditorFieldProps extends FieldProps {
schema?: JSONSchema7;
showSamples: boolean;
showShortcuts?: boolean;
showMiniMap?: boolean;
onSave?: () => void;
}

Expand Down
Expand Up @@ -131,3 +131,30 @@ Feature: Creation and Visualisation of serverless fuctions
Examples:
| git_url | workload_name |
| https://github.com/vikram-raj/hello-func-quarkus | hello-func-quarkus |


@regression @odc-7275
Scenario Outline: Test Serverless Functions: SF-01-TC10
Given user is at Add page
When user clicks on Create Serverless function card
And user enters git url "<git_url>"
And user clicks on Create button on Create Serverless function
And user sees workload "<workload_name>" along with a revision in topology page
And user clicks on the Knative Service workload "<workload_name>"
And user selects option "Test Serverless Function" from Actions menu
And user sees the Test Serverless Function modal
And user selects "<invoke_format>" from the Format drop down field
And user clicks on the "Advanced Settings" option
And user enters the "demo.fn" in the Type field
And user enters the "/demo/fn" in the Source field
And user clicks on Add optional headers and enter "Auth" under Name and "true" under Value
And user pastes the "request-body" code in "<invoke_format>" in the editor
And user clicks the "Test" Button
Then user is able to see a Success Alert
And user is able to see the Response Body as "response-body" code for the "<invoke_format>" format
And user clicks the "Close" Button

Examples:
| git_url | workload_name | invoke_format |
| https://github.com/openshift-dev-console/kn-func-typescript-http | kn-func-typescript-http | HTTP |
| https://github.com/openshift-dev-console/kn-func-typescript-cloudevents | kn-func-typescript-cloudevents | CloudEvent |
@@ -0,0 +1,82 @@
import { When, Then } from 'cypress-cucumber-preprocessor/steps';
import * as jsonEditor from '@console/cypress-integration-tests/views/yaml-editor';
import { topologyPO } from '@console/topology/integration-tests/support/page-objects/topology-po';
import { topologyPage } from '@console/topology/integration-tests/support/pages/topology';

When(
'user sees workload {string} along with a revision in topology page',
(workloadName: string) => {
topologyPage.verifyWorkloadInTopologyPage(workloadName);
cy.get(topologyPO.quickSearchPO.listView).click();
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000); /* Revision taking more time to appear than the default timeout */
cy.get(`[id$=${workloadName}]`).should('be.visible');
cy.get(topologyPO.quickSearchPO.graphView).click();
},
);

When('user selects option {string} from Actions menu', (action: string) => {
cy.byLegacyTestID('actions-menu-button').click();
cy.byTestActionID(action).should('be.visible').click();
});

When('user sees the Test Serverless Function modal', () => {
cy.byTestID('test-serverless-function').should('be.visible');
});

When('user selects {string} from the Format drop down field', (invokeFormat: string) => {
cy.byTestID('invoke-format-dropdown').should('be.visible').click();
cy.get(`[data-test-dropdown-menu="${invokeFormat}"]`).click();
});

When('user clicks on the "Advanced Settings" option', () => {
cy.byTestID('advanced-settings').find('.pf-c-expandable-section__toggle').click();
});

When('user enters the {string} in the Type field', (type: string) => {
cy.byTestID('request-type').click().type(type);
});
When('user enters the {string} in the Source field', (source: string) => {
cy.byTestID('request-source').click().type(source);
});

When(
'user clicks on Add optional headers and enter {string} under Name and {string} under Value',
(name, value: string) => {
cy.byTestID('add-optional-header').click();
cy.byTestID('pairs-list-name').click().type(name);
cy.byTestID('pairs-list-value').click().type(value);
},
);

When(
'user pastes the {string} code in {string} in the editor',
(jsonFile: string, invokeFormat: string) => {
cy.fixture(`test-serverless-fn/${jsonFile}-${invokeFormat.toLowerCase()}.json`).then((json) => {
jsonEditor.setEditorContent(JSON.stringify(json, null, 2));
});
},
);

When('user clicks the {string} Button', (button: string) => {
cy.byTestID(`${button.toLowerCase()}-action`).should('be.visible').click();
});

Then('user is able to see a Success Alert', () => {
cy.byTestID('alert-wait').should('not.exist');
cy.byTestID('alert-success').should('be.visible');
});

Then(
'user is able to see the Response Body as {string} code for the {string} format',
(jsonFile: string, invokeFormat: string) => {
cy.byTestID('loading-indicator').should('not.exist');
jsonEditor.getEditorContent().then((content) => {
cy.fixture(`test-serverless-fn/${jsonFile}-${invokeFormat.toLowerCase()}.json`).then(
(json) => {
(expect(JSON.parse(content)) as any).to.deep.equal(json);
},
);
});
},
);
@@ -0,0 +1,3 @@
{
"message": "Hello CloudEvents!"
}
@@ -0,0 +1,3 @@
{
"message": "Hello HTTP!"
}
@@ -0,0 +1,3 @@
{
"message": "Hello CloudEvents!"
}
@@ -0,0 +1,3 @@
{
"message": "Hello HTTP!"
}
21 changes: 12 additions & 9 deletions frontend/packages/knative-plugin/locales/en/knative-plugin.json
Expand Up @@ -246,21 +246,24 @@
"Select a sink": "Select a sink",
"Save": "Save",
"Editing this URI will affect all associated Event Sources.": "Editing this URI will affect all associated Event Sources.",
"Request": "Request",
"Response": "Response",
"Body": "Body",
"Options": "Options",
"Info": "Info",
"CloudEvent": "CloudEvent",
"HTTP": "HTTP",
"application/json": "application/json",
"application/yaml": "application/yaml",
"Format": "Format",
"Content-Type": "Content-Type",
"Advanced Settings": "Advanced Settings",
"Add headers": "Add headers",
"Add optional headers": "Add optional headers",
"Status": "Status",
"Response Header": "Response Header",
"Invoke": "Invoke",
"Invokes the function by sending a test request to the currently running function instance": "Invokes the function by sending a test request to the currently running function instance",
"Waiting for response": "Waiting for response",
"The Test was Successful": "The Test was Successful",
"Response Status Code: ": "Response Status Code: ",
"An error occurred": "An error occurred",
"Show Response Headers": "Show Response Headers",
"Hide Response Headers": "Hide Response Headers",
"Test": "Test",
"Back": "Back",
"Close": "Close",
"Add Revision": "Add Revision",
"Split": "Split",
"Tag": "Tag",
Expand Down
9 changes: 1 addition & 8 deletions frontend/packages/knative-plugin/src/actions/providers.ts
Expand Up @@ -29,7 +29,6 @@ import {
EVENT_SINK_CATALOG_TYPE_ID,
EVENT_SOURCE_ACTION_ID,
EVENT_SOURCE_CATALOG_TYPE_ID,
KNATIVE_SERVERLESS_FUNCTION_TEST,
} from '../const';
import { RevisionModel, EventingBrokerModel, ServiceModel } from '../models';
import {
Expand Down Expand Up @@ -88,11 +87,6 @@ export const useSinkPubSubActionProvider = (resource: K8sResourceKind) => {
};

export const useKnativeServiceActionsProvider = (resource: K8sResourceKind) => {
/* For testing purpose only */
if (localStorage.getItem(KNATIVE_SERVERLESS_FUNCTION_TEST) === null) {
localStorage.setItem(KNATIVE_SERVERLESS_FUNCTION_TEST, 'false');
}

const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
const actions = React.useMemo(() => {
return [
Expand All @@ -106,8 +100,7 @@ export const useKnativeServiceActionsProvider = (resource: K8sResourceKind) => {
...(resource.metadata.annotations?.['openshift.io/generated-by'] === 'OpenShiftWebConsole'
? [DeleteResourceAction(kindObj, resource)]
: [CommonActionFactory.Delete(kindObj, resource)]),
...(resource?.metadata?.labels?.['function.knative.dev'] === 'true' &&
localStorage.getItem(KNATIVE_SERVERLESS_FUNCTION_TEST) === 'true'
...(resource?.metadata?.labels?.['function.knative.dev'] === 'true'
? [testServerlessFunction(kindObj, resource)]
: []),
];
Expand Down