mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-19 22:49:00 +00:00
Application cannot be installed with None as schedule type (#15524)
* Application cannot be installed with None as schedule type * MINOR: Add / Fix GCS and ADLS - docs, bugs (#15502) Add GCS and ADLS docs * Add stack trace while throwing an error to debug (#15522) * Fix #15533: Fix name & display name for kafka json schema parser (#15534) * MINOR: postgres add ssl options in yaml (#15538) * Small change to fix the Glossary TErm Tasks not fetching Owner automatically (#15535) * remove the DataInsightAlert spec as the alerts test are covered in ObservabilityAlerts spec (#15531) * MINOR: fix help dropdown item font and icon sizes (#15511) * fix help dropdown item font and icon sizes * added unit test for navbarUtils * fix sonar failure * changes as per comments * minor fixes * Minor: fixed DQ edit test case issue and searchIndexDetails typescript issue (#15528) * Minor: fixed DQ edit test case issue and searchIndexDetails typescript issue * fixed failing unit test * fixed unit test for Data quality test * reverting e2eLabeler changes * chore(ui): separate routes as per categories (#15512) * chore(ui): seprate routes as per categories * add tests * fix test * domain path fix * [MINOR] GX logging hierarchy (#15542) * fix: GX module logging hierarchy * style: ran python linting * - Use Entity Type (#15546) * removed docs 1.4.x (#15550) * Suggestions Alert new design (#15532) * update ux of suggestions alert * locales * minor changes * fix descriptions * minor css fixes * Fix #11868: Duplicated queries cannot be created (#15519) * Fix #11868: Duplicate query should throw an error of entityExists * Fix #11868: Duplicate query should throw an error of entityExists * fix test * fix test * Fix uniquee constraint for checksum in Postgres --------- Co-authored-by: Pere Miquel Brull <peremiquelbrull@gmail.com> * added OpenMetadataApplication.getDao (#15549) * Add the entityUrl to the header for ThreadMessages (#15552) * feat: AppResource (#15555) forbid modification of system app * MINOR: Skip source hash generation for service (#15516) * sync the documentation roadmap page with the getcollate roadmap (#15551) * Application cannot be installed with None as schedule type * None type should not register in scheduler * scheduleTimeline fix * fix payload of schedule type none and change it to scheduleTimeline * fix unit test --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io> Co-authored-by: Ayush Shah <ayush@getcollate.io> Co-authored-by: Mayur Singal <39544459+ulixius9@users.noreply.github.com> Co-authored-by: IceS2 <pjt1991@gmail.com> Co-authored-by: Aniket Katkar <aniketkatkar97@gmail.com> Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com> Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Co-authored-by: Teddy <teddy.crepineau@gmail.com> Co-authored-by: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com> Co-authored-by: Imri Paran <imri.paran@gmail.com> Co-authored-by: Karan Hotchandani <33024356+karanh37@users.noreply.github.com> Co-authored-by: Pere Miquel Brull <peremiquelbrull@gmail.com> Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
This commit is contained in:
parent
b52fdaf05e
commit
8377128c93
@ -7,7 +7,6 @@ ALTER TABLE query_entity ADD COLUMN checksum VARCHAR(32) GENERATED ALWAYS AS (js
|
||||
|
||||
UPDATE query_entity SET json = JSON_INSERT(json, '$.checksum', MD5(JSON_UNQUOTE(JSON_EXTRACT(json, '$.checksum'))));
|
||||
|
||||
|
||||
-- Restructure dbServiceNames in ingestion_pipeline_entity
|
||||
update ingestion_pipeline_entity set json =
|
||||
JSON_INSERT(
|
||||
@ -68,3 +67,12 @@ ALTER TABLE user_entity ADD INDEX index_user_entity_deleted(nameHash, deleted);
|
||||
ALTER TABLE apps_extension_time_series ADD INDEX apps_extension_time_series_index(appId);
|
||||
ALTER TABLE suggestions ADD INDEX index_suggestions_type(suggestionType);
|
||||
ALTER TABLE suggestions ADD INDEX index_suggestions_status(status);
|
||||
|
||||
-- Change scheduleType to scheduleTimeline
|
||||
UPDATE installed_apps
|
||||
SET json = JSON_INSERT(
|
||||
JSON_REMOVE(json, '$.appSchedule.scheduleType'),
|
||||
'$.appSchedule.scheduleTimeline',
|
||||
JSON_EXTRACT(json, '$.appSchedule.scheduleType')
|
||||
);
|
||||
delete from apps_extension_time_series;
|
||||
|
@ -67,3 +67,12 @@ CREATE INDEX apps_extension_time_series_index ON apps_extension_time_series (app
|
||||
CREATE INDEX index_suggestions_type ON suggestions (suggestionType);
|
||||
CREATE INDEX index_suggestions_status ON suggestions (status);
|
||||
|
||||
-- change scheduleType to scheduleTimeline
|
||||
UPDATE installed_apps
|
||||
SET json = jsonb_set(
|
||||
json::jsonb #- '{appSchedule,scheduleType}',
|
||||
'{appSchedule,scheduleTimeline}',
|
||||
(json #> '{appSchedule,scheduleType}')::jsonb,
|
||||
true
|
||||
);
|
||||
delete from apps_extension_time_series;
|
||||
|
@ -13,6 +13,7 @@ import org.openmetadata.schema.api.services.ingestionPipelines.CreateIngestionPi
|
||||
import org.openmetadata.schema.entity.app.App;
|
||||
import org.openmetadata.schema.entity.app.AppRunRecord;
|
||||
import org.openmetadata.schema.entity.app.AppType;
|
||||
import org.openmetadata.schema.entity.app.ScheduleTimeline;
|
||||
import org.openmetadata.schema.entity.app.ScheduleType;
|
||||
import org.openmetadata.schema.entity.app.ScheduledExecutionContext;
|
||||
import org.openmetadata.schema.entity.applications.configuration.ApplicationConfig;
|
||||
@ -61,6 +62,11 @@ public class AbstractNativeApplication implements NativeApplication {
|
||||
|
||||
@Override
|
||||
public void install() {
|
||||
// If the app does not have any Schedule Return without scheduling
|
||||
if (app.getAppSchedule() != null
|
||||
&& app.getAppSchedule().getScheduleTimeline().equals(ScheduleTimeline.NONE)) {
|
||||
return;
|
||||
}
|
||||
if (app.getAppType() == AppType.Internal
|
||||
&& app.getScheduleType().equals(ScheduleType.Scheduled)) {
|
||||
scheduleInternal();
|
||||
|
@ -19,6 +19,7 @@ import org.openmetadata.schema.AppRuntime;
|
||||
import org.openmetadata.schema.entity.app.App;
|
||||
import org.openmetadata.schema.entity.app.AppRunType;
|
||||
import org.openmetadata.schema.entity.app.AppSchedule;
|
||||
import org.openmetadata.schema.entity.app.ScheduleTimeline;
|
||||
import org.openmetadata.service.OpenMetadataApplicationConfig;
|
||||
import org.openmetadata.service.apps.NativeApplication;
|
||||
import org.openmetadata.service.exception.UnhandledServerException;
|
||||
@ -142,8 +143,14 @@ public class AppScheduler {
|
||||
AppRuntime context = getAppRuntime(application);
|
||||
if (Boolean.TRUE.equals(context.getEnabled())) {
|
||||
JobDetail jobDetail = jobBuilder(application, application.getName());
|
||||
Trigger trigger = trigger(application);
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
if (!application
|
||||
.getAppSchedule()
|
||||
.getScheduleTimeline()
|
||||
.value()
|
||||
.equals(ScheduleTimeline.NONE)) {
|
||||
Trigger trigger = trigger(application);
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
}
|
||||
} else {
|
||||
LOG.info("[Applications] App cannot be scheduled since it is disabled");
|
||||
}
|
||||
@ -171,7 +178,7 @@ public class AppScheduler {
|
||||
private JobDetail jobBuilder(App app, String jobIdentity) throws ClassNotFoundException {
|
||||
JobDataMap dataMap = new JobDataMap();
|
||||
dataMap.put(APP_INFO_KEY, JsonUtils.pojoToJson(app));
|
||||
dataMap.put("triggerType", AppRunType.Scheduled.value());
|
||||
dataMap.put("triggerType", app.getAppSchedule().getScheduleTimeline().value());
|
||||
Class<? extends NativeApplication> clz =
|
||||
(Class<? extends NativeApplication>) Class.forName(app.getClassName());
|
||||
JobBuilder jobBuilder =
|
||||
@ -196,7 +203,7 @@ public class AppScheduler {
|
||||
}
|
||||
|
||||
public static CronScheduleBuilder getCronSchedule(AppSchedule scheduleInfo) {
|
||||
switch (scheduleInfo.getScheduleType()) {
|
||||
switch (scheduleInfo.getScheduleTimeline()) {
|
||||
case HOURLY:
|
||||
return CronScheduleBuilder.cronSchedule("0 0 * ? * *");
|
||||
case DAILY:
|
||||
|
@ -39,6 +39,7 @@ import org.openmetadata.schema.EntityInterface;
|
||||
import org.openmetadata.schema.ServiceEntityInterface;
|
||||
import org.openmetadata.schema.entity.app.App;
|
||||
import org.openmetadata.schema.entity.app.AppSchedule;
|
||||
import org.openmetadata.schema.entity.app.ScheduleTimeline;
|
||||
import org.openmetadata.schema.entity.app.ScheduledExecutionContext;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline;
|
||||
import org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection;
|
||||
@ -254,8 +255,7 @@ public class OpenMetadataOperations implements Callable<Integer> {
|
||||
.withId(UUID.randomUUID())
|
||||
.withName("SearchIndexApp")
|
||||
.withClassName("org.openmetadata.service.apps.bundles.searchIndex.SearchIndexApp")
|
||||
.withAppSchedule(
|
||||
new AppSchedule().withScheduleType(AppSchedule.ScheduleTimeline.DAILY))
|
||||
.withAppSchedule(new AppSchedule().withScheduleTimeline(ScheduleTimeline.DAILY))
|
||||
.withAppConfiguration(
|
||||
new EventPublisherJob()
|
||||
.withEntities(new HashSet<>(List.of("all")))
|
||||
|
@ -3,7 +3,7 @@
|
||||
"displayName": "Data Insights",
|
||||
"appConfiguration": {},
|
||||
"appSchedule": {
|
||||
"scheduleType": "Custom",
|
||||
"scheduleTimeline": "Custom",
|
||||
"cronExpression": "0 0 1/1 * *"
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@
|
||||
"searchIndexMappingLanguage": "EN"
|
||||
},
|
||||
"appSchedule": {
|
||||
"scheduleType": "Custom",
|
||||
"scheduleTimeline": "Custom",
|
||||
"cronExpression": "0 0 * * *"
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import org.openmetadata.schema.entity.app.AppMarketPlaceDefinition;
|
||||
import org.openmetadata.schema.entity.app.AppSchedule;
|
||||
import org.openmetadata.schema.entity.app.CreateApp;
|
||||
import org.openmetadata.schema.entity.app.CreateAppMarketPlaceDefinitionReq;
|
||||
import org.openmetadata.schema.entity.app.ScheduleTimeline;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.exception.EntityNotFoundException;
|
||||
import org.openmetadata.service.resources.EntityResourceTest;
|
||||
@ -51,7 +52,7 @@ public class AppsResourceTest extends EntityResourceTest<App, CreateApp> {
|
||||
return new CreateApp()
|
||||
.withName(appMarketPlaceDefinition.getName())
|
||||
.withAppConfiguration(appMarketPlaceDefinition.getAppConfiguration())
|
||||
.withAppSchedule(new AppSchedule().withScheduleType(AppSchedule.ScheduleTimeline.HOURLY));
|
||||
.withAppSchedule(new AppSchedule().withScheduleTimeline(ScheduleTimeline.HOURLY));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -25,15 +25,17 @@
|
||||
]
|
||||
},
|
||||
"scheduleTimeline": {
|
||||
"javaType": "org.openmetadata.schema.entity.app.ScheduleTimeline",
|
||||
"description": "This schema defines the Application ScheduleTimeline Options",
|
||||
"type": "string",
|
||||
"enum": ["Hourly"," Daily", "Weekly", "Monthly", "Custom"],
|
||||
"enum": ["Hourly"," Daily", "Weekly", "Monthly", "Custom", "None"],
|
||||
"default": "Weekly"
|
||||
},
|
||||
"appSchedule": {
|
||||
"javaType": "org.openmetadata.schema.entity.app.AppSchedule",
|
||||
"description": "This schema defines the type of application.",
|
||||
"properties": {
|
||||
"scheduleType": {
|
||||
"scheduleTimeline": {
|
||||
"$ref": "#/definitions/scheduleTimeline"
|
||||
},
|
||||
"cronExpression": {
|
||||
@ -41,7 +43,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["scheduleType"],
|
||||
"required": ["scheduleTimeline"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"appType": {
|
||||
|
@ -32,7 +32,7 @@ import {
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { AxiosError } from 'axios';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { noop } from 'lodash';
|
||||
import { isEmpty, noop } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@ -273,8 +273,10 @@ const AppDetails = () => {
|
||||
const updatedData = {
|
||||
...appData,
|
||||
appSchedule: {
|
||||
scheduleType: ScheduleTimeline.Custom,
|
||||
cronExpression: cron,
|
||||
scheduleTimeline: isEmpty(cron)
|
||||
? ScheduleTimeline.None
|
||||
: ScheduleTimeline.Custom,
|
||||
...(cron ? { cronExpression: cron } : {}),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -58,7 +58,7 @@ const mockProps1 = {
|
||||
},
|
||||
},
|
||||
scheduleInfo: {
|
||||
scheduleType: ScheduleTimeline.Custom,
|
||||
scheduleTimeline: ScheduleTimeline.Custom,
|
||||
cronExpression: '0 0 0 1/1 * ? *',
|
||||
},
|
||||
id: '6e4d3dcf-238d-4874-b4e4-dd863ede6544-OnDemand-1706871884587',
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { Button, Col, Divider, Modal, Row, Space, Typography } from 'antd';
|
||||
import cronstrue from 'cronstrue';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@ -68,11 +69,10 @@ const AppSchedule = ({
|
||||
}, [appData]);
|
||||
|
||||
const cronString = useMemo(() => {
|
||||
if (appData.appSchedule) {
|
||||
const cronExp =
|
||||
(appData.appSchedule as AppScheduleClass).cronExpression ?? '';
|
||||
|
||||
return cronstrue.toString(cronExp, {
|
||||
const cronExpression = (appData.appSchedule as AppScheduleClass)
|
||||
?.cronExpression;
|
||||
if (cronExpression) {
|
||||
return cronstrue.toString(cronExpression, {
|
||||
throwExceptionOnParseError: false,
|
||||
});
|
||||
}
|
||||
@ -150,19 +150,18 @@ const AppSchedule = ({
|
||||
<Col className="flex-col" flex="auto">
|
||||
{appData.appSchedule && (
|
||||
<>
|
||||
<div>
|
||||
<Space size={8}>
|
||||
<Typography.Text className="right-panel-label">
|
||||
{t('label.schedule-type')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="font-medium">
|
||||
{(appData.appSchedule as AppScheduleClass).scheduleType ??
|
||||
''}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<div className="d-flex items-center gap-2">
|
||||
<Typography.Text className="right-panel-label">
|
||||
{t('label.schedule-type')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="font-medium">
|
||||
{(appData.appSchedule as AppScheduleClass).scheduleTimeline ??
|
||||
''}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<Space size={8}>
|
||||
|
||||
{!isEmpty(cronString) && (
|
||||
<div className="d-flex items-center gap-2">
|
||||
<Typography.Text className="right-panel-label">
|
||||
{t('label.schedule-interval')}
|
||||
</Typography.Text>
|
||||
@ -171,8 +170,8 @@ const AppSchedule = ({
|
||||
data-testid="cron-string">
|
||||
{cronString}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
@ -231,7 +230,7 @@ const AppSchedule = ({
|
||||
}}
|
||||
includePeriodOptions={initialOptions}
|
||||
initialData={
|
||||
(appData.appSchedule as AppScheduleClass)?.cronExpression ?? ''
|
||||
(appData.appSchedule as AppScheduleClass)?.cronExpression
|
||||
}
|
||||
isLoading={isSaveLoading}
|
||||
onCancel={onDialogCancel}
|
||||
|
@ -97,7 +97,7 @@ export const mockApplicationData = {
|
||||
},
|
||||
pipelines: [],
|
||||
appSchedule: {
|
||||
scheduleType: ScheduleTimeline.Custom,
|
||||
scheduleTimeline: ScheduleTimeline.Custom,
|
||||
cronExpression: '0 0 0 1/1 * ? *',
|
||||
},
|
||||
appScreenshots: ['SearchIndexPic1.png'],
|
||||
|
@ -15,6 +15,7 @@ import { RJSFSchema } from '@rjsf/utils';
|
||||
import validator from '@rjsf/validator-ajv8';
|
||||
import { Col, Row, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@ -118,8 +119,10 @@ const AppInstall = () => {
|
||||
const data: CreateAppRequest = {
|
||||
appConfiguration: appConfiguration ?? appData?.appConfiguration,
|
||||
appSchedule: {
|
||||
scheduleType: ScheduleTimeline.Custom,
|
||||
cronExpression: repeatFrequency,
|
||||
scheduleTimeline: isEmpty(repeatFrequency)
|
||||
? ScheduleTimeline.None
|
||||
: ScheduleTimeline.Custom,
|
||||
...(repeatFrequency ? { cronExpression: repeatFrequency } : {}),
|
||||
},
|
||||
name: fqn,
|
||||
description: appData?.description,
|
||||
|
@ -295,7 +295,9 @@ const LogsViewer = () => {
|
||||
|
||||
return {
|
||||
Type:
|
||||
ingestionDetails?.pipelineType ?? scheduleClass?.scheduleType ?? '--',
|
||||
ingestionDetails?.pipelineType ??
|
||||
scheduleClass?.scheduleTimeline ??
|
||||
'--',
|
||||
Schedule:
|
||||
ingestionDetails?.airflowConfig.scheduleInterval ??
|
||||
scheduleClass?.cronExpression ??
|
||||
|
Loading…
x
Reference in New Issue
Block a user