Initial import into Gitea
This commit is contained in:
257
Gateway/Migrations/007_ProviderStatusMap.sql
Normal file
257
Gateway/Migrations/007_ProviderStatusMap.sql
Normal file
@@ -0,0 +1,257 @@
|
||||
-- ============================================================
|
||||
-- Provider Status Mapping Reference Table
|
||||
-- ============================================================
|
||||
-- Maps provider-specific campaign statuses to platform statuses.
|
||||
-- Runtime normalization is config-driven (appsettings.json),
|
||||
-- but this table serves as:
|
||||
-- 1. Canonical reference / documentation
|
||||
-- 2. Admin-editable override (future phase)
|
||||
-- 3. Audit trail for mapping changes
|
||||
--
|
||||
-- Platform statuses: draft, staged, pending, active, paused,
|
||||
-- completed, cancelled, error
|
||||
-- ============================================================
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = 'tbProviderStatusMap')
|
||||
BEGIN
|
||||
CREATE TABLE dbo.tbProviderStatusMap (
|
||||
psmId INT IDENTITY(1,1) PRIMARY KEY,
|
||||
psmChannelType VARCHAR(50) NOT NULL, -- google_ads, meta, tiktok
|
||||
psmProviderStatus VARCHAR(100) NOT NULL, -- raw provider value (ENABLED, DELIVERY_OK, etc.)
|
||||
psmPlatformStatus VARCHAR(20) NOT NULL, -- normalized platform value
|
||||
psmDescription NVARCHAR(200) NULL, -- human-readable explanation
|
||||
psmIsActive BIT NOT NULL DEFAULT 1,
|
||||
psmCreatedAt DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
|
||||
psmUpdatedAt DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
|
||||
|
||||
CONSTRAINT UQ_ProviderStatusMap_Channel_Status
|
||||
UNIQUE (psmChannelType, psmProviderStatus),
|
||||
|
||||
CONSTRAINT CK_ProviderStatusMap_PlatformStatus
|
||||
CHECK (psmPlatformStatus IN ('draft','staged','pending','active','paused','completed','cancelled','error'))
|
||||
);
|
||||
|
||||
CREATE NONCLUSTERED INDEX IX_ProviderStatusMap_Channel
|
||||
ON dbo.tbProviderStatusMap (psmChannelType)
|
||||
INCLUDE (psmProviderStatus, psmPlatformStatus)
|
||||
WHERE psmIsActive = 1;
|
||||
END
|
||||
GO
|
||||
|
||||
-- ============================================================
|
||||
-- Seed: Google Ads
|
||||
-- ============================================================
|
||||
MERGE dbo.tbProviderStatusMap AS tgt
|
||||
USING (VALUES
|
||||
('google_ads', 'ENABLED', 'active', 'Campaign is serving ads'),
|
||||
('google_ads', 'Enabled', 'active', 'Campaign is serving ads (camelCase variant)'),
|
||||
('google_ads', 'PAUSED', 'paused', 'Campaign is paused by advertiser'),
|
||||
('google_ads', 'Paused', 'paused', 'Campaign is paused (camelCase variant)'),
|
||||
('google_ads', 'REMOVED', 'cancelled', 'Campaign has been removed'),
|
||||
('google_ads', 'Removed', 'cancelled', 'Campaign has been removed (camelCase variant)'),
|
||||
('google_ads', 'UNKNOWN', 'error', 'Unknown status from Google Ads API'),
|
||||
('google_ads', 'UNSPECIFIED', 'error', 'Unspecified status from Google Ads API')
|
||||
) AS src (channelType, providerStatus, platformStatus, description)
|
||||
ON tgt.psmChannelType = src.channelType AND tgt.psmProviderStatus = src.providerStatus
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (psmChannelType, psmProviderStatus, psmPlatformStatus, psmDescription)
|
||||
VALUES (src.channelType, src.providerStatus, src.platformStatus, src.description);
|
||||
GO
|
||||
|
||||
-- ============================================================
|
||||
-- Seed: Meta (Facebook / Instagram)
|
||||
-- ============================================================
|
||||
MERGE dbo.tbProviderStatusMap AS tgt
|
||||
USING (VALUES
|
||||
('meta', 'ACTIVE', 'active', 'Campaign is delivering'),
|
||||
('meta', 'PAUSED', 'paused', 'Campaign paused by advertiser'),
|
||||
('meta', 'DELETED', 'cancelled', 'Campaign deleted'),
|
||||
('meta', 'ARCHIVED', 'completed', 'Campaign archived after completion'),
|
||||
('meta', 'IN_PROCESS', 'pending', 'Campaign is being processed'),
|
||||
('meta', 'WITH_ISSUES', 'error', 'Campaign has delivery issues'),
|
||||
('meta', 'CAMPAIGN_PAUSED', 'paused', 'Parent campaign is paused'),
|
||||
('meta', 'ADSET_PAUSED', 'paused', 'Ad set level pause'),
|
||||
('meta', 'DISAPPROVED', 'error', 'Ad/campaign disapproved by review'),
|
||||
('meta', 'PREAPPROVED', 'pending', 'Preapproved, awaiting final review'),
|
||||
('meta', 'PENDING_REVIEW', 'pending', 'Awaiting Meta ad review'),
|
||||
('meta', 'PENDING_BILLING_INFO','error', 'Billing information required')
|
||||
) AS src (channelType, providerStatus, platformStatus, description)
|
||||
ON tgt.psmChannelType = src.channelType AND tgt.psmProviderStatus = src.providerStatus
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (psmChannelType, psmProviderStatus, psmPlatformStatus, psmDescription)
|
||||
VALUES (src.channelType, src.providerStatus, src.platformStatus, src.description);
|
||||
GO
|
||||
|
||||
-- ============================================================
|
||||
-- Seed: TikTok
|
||||
-- ============================================================
|
||||
MERGE dbo.tbProviderStatusMap AS tgt
|
||||
USING (VALUES
|
||||
('tiktok', 'ENABLE', 'active', 'Campaign is active and delivering'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_ENABLE', 'active', 'Campaign enabled (prefixed variant)'),
|
||||
('tiktok', 'DISABLE', 'paused', 'Campaign disabled by advertiser'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_DISABLE', 'paused', 'Campaign disabled (prefixed variant)'),
|
||||
('tiktok', 'DELETE', 'cancelled', 'Campaign deleted'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_DELETE', 'cancelled', 'Campaign deleted (prefixed variant)'),
|
||||
('tiktok', 'BUDGET_EXCEED', 'paused', 'Budget limit exceeded'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_BUDGET_EXCEED', 'paused', 'Budget exceeded (prefixed variant)'),
|
||||
('tiktok', 'ADVERTISER_AUDIT_DENY', 'error', 'Advertiser account audit denied'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_ADVERTISER_AUDIT_DENY','error', 'Audit denied (prefixed variant)'),
|
||||
('tiktok', 'NOT_DELETE', 'active', 'Campaign exists and is not deleted'),
|
||||
('tiktok', 'ADVERTISER_AUDIT', 'pending', 'Advertiser account under audit'),
|
||||
('tiktok', 'CAMPAIGN_STATUS_ADVERTISER_AUDIT', 'pending', 'Under audit (prefixed variant)'),
|
||||
('tiktok', 'REAUDIT', 'pending', 'Campaign under re-audit'),
|
||||
('tiktok', 'ALL', 'active', 'TikTok ALL filter status (treat as active)')
|
||||
) AS src (channelType, providerStatus, platformStatus, description)
|
||||
ON tgt.psmChannelType = src.channelType AND tgt.psmProviderStatus = src.providerStatus
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (psmChannelType, psmProviderStatus, psmPlatformStatus, psmDescription)
|
||||
VALUES (src.channelType, src.providerStatus, src.platformStatus, src.description);
|
||||
GO
|
||||
|
||||
-- ============================================================
|
||||
-- Seed: Common / Internal (platform-generated statuses)
|
||||
-- ============================================================
|
||||
MERGE dbo.tbProviderStatusMap AS tgt
|
||||
USING (VALUES
|
||||
('_common', 'submitted', 'active', 'Successfully dispatched to provider'),
|
||||
('_common', 'pending_review', 'pending', 'Awaiting provider review'),
|
||||
('_common', 'stub_provider', 'pending', 'Stub provider — no real dispatch yet'),
|
||||
('_common', 'approved', 'active', 'Provider approved the campaign'),
|
||||
('_common', 'rejected', 'error', 'Provider rejected the campaign'),
|
||||
('_common', 'suspended', 'paused', 'Campaign suspended by provider'),
|
||||
('_common', 'budget_depleted', 'paused', 'Budget fully consumed'),
|
||||
('_common', 'expired', 'completed', 'Campaign reached its end date'),
|
||||
('_common', 'archived', 'completed', 'Campaign archived'),
|
||||
('_common', 'deleted', 'cancelled', 'Campaign deleted'),
|
||||
('_common', 'in_process', 'pending', 'Campaign is being processed'),
|
||||
('_common', 'in_review', 'pending', 'Campaign is under review'),
|
||||
('_common', 'learning', 'active', 'Campaign in learning/optimization phase'),
|
||||
('_common', 'limited', 'active', 'Campaign serving but limited (budget, targeting)')
|
||||
) AS src (channelType, providerStatus, platformStatus, description)
|
||||
ON tgt.psmChannelType = src.channelType AND tgt.psmProviderStatus = src.providerStatus
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (psmChannelType, psmProviderStatus, psmPlatformStatus, psmDescription)
|
||||
VALUES (src.channelType, src.providerStatus, src.platformStatus, src.description);
|
||||
GO
|
||||
|
||||
-- ============================================================
|
||||
-- Stored Procedure: spProviderStatusMap
|
||||
-- ============================================================
|
||||
-- Actions: list, get, upsert, delete
|
||||
-- Follows standard JSON request/response pattern.
|
||||
-- ============================================================
|
||||
CREATE OR ALTER PROCEDURE dbo.spProviderStatusMap
|
||||
@Action VARCHAR(20),
|
||||
@Rqst NVARCHAR(MAX) = '{}',
|
||||
@Resp NVARCHAR(MAX) OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- ── LIST ──
|
||||
IF @Action = 'list'
|
||||
BEGIN
|
||||
DECLARE @filterChannel VARCHAR(50) = JSON_VALUE(@Rqst, '$.channelType');
|
||||
|
||||
SET @Resp = (
|
||||
SELECT
|
||||
psmId AS id,
|
||||
psmChannelType AS channelType,
|
||||
psmProviderStatus AS providerStatus,
|
||||
psmPlatformStatus AS platformStatus,
|
||||
psmDescription AS [description],
|
||||
psmIsActive AS isActive
|
||||
FROM dbo.tbProviderStatusMap
|
||||
WHERE psmIsActive = 1
|
||||
AND (@filterChannel IS NULL OR psmChannelType = @filterChannel)
|
||||
ORDER BY psmChannelType, psmProviderStatus
|
||||
FOR JSON PATH, ROOT('data')
|
||||
);
|
||||
|
||||
IF @Resp IS NULL SET @Resp = '{"data":[]}';
|
||||
SET @Resp = '{"ok":true,' + SUBSTRING(@Resp, 2, LEN(@Resp));
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── GET ──
|
||||
IF @Action = 'get'
|
||||
BEGIN
|
||||
DECLARE @getId INT = JSON_VALUE(@Rqst, '$.id');
|
||||
|
||||
SET @Resp = (
|
||||
SELECT
|
||||
psmId AS id,
|
||||
psmChannelType AS channelType,
|
||||
psmProviderStatus AS providerStatus,
|
||||
psmPlatformStatus AS platformStatus,
|
||||
psmDescription AS [description],
|
||||
psmIsActive AS isActive
|
||||
FROM dbo.tbProviderStatusMap
|
||||
WHERE psmId = @getId
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
);
|
||||
|
||||
IF @Resp IS NULL
|
||||
BEGIN
|
||||
SET @Resp = '{"ok":false,"error":"Mapping not found"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
SET @Resp = '{"ok":true,"data":' + @Resp + '}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── UPSERT ──
|
||||
IF @Action = 'upsert'
|
||||
BEGIN
|
||||
DECLARE @uChannelType VARCHAR(50) = JSON_VALUE(@Rqst, '$.channelType');
|
||||
DECLARE @uProviderStatus VARCHAR(100) = JSON_VALUE(@Rqst, '$.providerStatus');
|
||||
DECLARE @uPlatformStatus VARCHAR(20) = JSON_VALUE(@Rqst, '$.platformStatus');
|
||||
DECLARE @uDescription NVARCHAR(200) = JSON_VALUE(@Rqst, '$.description');
|
||||
|
||||
IF @uChannelType IS NULL OR @uProviderStatus IS NULL OR @uPlatformStatus IS NULL
|
||||
BEGIN
|
||||
SET @Resp = '{"ok":false,"error":"channelType, providerStatus, and platformStatus are required"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
IF @uPlatformStatus NOT IN ('draft','staged','pending','active','paused','completed','cancelled','error')
|
||||
BEGIN
|
||||
SET @Resp = '{"ok":false,"error":"Invalid platformStatus. Must be: draft, staged, pending, active, paused, completed, cancelled, error"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
MERGE dbo.tbProviderStatusMap AS tgt
|
||||
USING (SELECT @uChannelType, @uProviderStatus) AS src (ct, ps)
|
||||
ON tgt.psmChannelType = src.ct AND tgt.psmProviderStatus = src.ps
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET
|
||||
psmPlatformStatus = @uPlatformStatus,
|
||||
psmDescription = COALESCE(@uDescription, psmDescription),
|
||||
psmIsActive = 1,
|
||||
psmUpdatedAt = SYSUTCDATETIME()
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (psmChannelType, psmProviderStatus, psmPlatformStatus, psmDescription)
|
||||
VALUES (@uChannelType, @uProviderStatus, @uPlatformStatus, @uDescription);
|
||||
|
||||
SET @Resp = '{"ok":true,"message":"Mapping saved"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── DELETE (soft) ──
|
||||
IF @Action = 'delete'
|
||||
BEGIN
|
||||
DECLARE @dId INT = JSON_VALUE(@Rqst, '$.id');
|
||||
|
||||
UPDATE dbo.tbProviderStatusMap
|
||||
SET psmIsActive = 0, psmUpdatedAt = SYSUTCDATETIME()
|
||||
WHERE psmId = @dId;
|
||||
|
||||
SET @Resp = '{"ok":true,"message":"Mapping deactivated"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
SET @Resp = '{"ok":false,"error":"Unknown action: ' + ISNULL(@Action,'null') + '"}';
|
||||
END
|
||||
GO
|
||||
Reference in New Issue
Block a user