Skip to content

Commit 2667b4c

Browse files
committed
Merge branch 'development' into release/2.19.1
* development: fix: fix terms feat: version bumped fix: fixing scheduler build improve variable and method names for retrieving dataStores refactor method to generate ns keys replace dataStore if namespace is selected fix select/unselect checkbox
2 parents 5ec48a5 + 9fee844 commit 2667b4c

File tree

14 files changed

+184
-137
lines changed

14 files changed

+184
-137
lines changed

i18n/en.pot

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ msgstr ""
55
"Content-Type: text/plain; charset=utf-8\n"
66
"Content-Transfer-Encoding: 8bit\n"
77
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
8-
"POT-Creation-Date: 2024-07-26T07:11:17.572Z\n"
9-
"PO-Revision-Date: 2024-07-26T07:11:17.572Z\n"
8+
"POT-Creation-Date: 2024-09-09T11:54:49.799Z\n"
9+
"PO-Revision-Date: 2024-09-09T11:54:49.799Z\n"
1010

1111
msgid ""
1212
"THIS NEW RELEASE INCLUDES SHARING SETTINGS PER INSTANCES. FOR THIS VERSION "

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"rehype-sanitize": "^5.0.1",
5656
"rxjs": "5.5.7",
5757
"semver": "7.5.2",
58-
"styled-components": "6.1.11",
58+
"styled-components": "6.1.8",
5959
"styled-jsx": "5.0.0"
6060
},
6161
"scripts": {

src/data/common/D2ApiDataStore.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
1-
import _ from "lodash";
21
import { D2Api } from "../../types/d2-api";
3-
import { DataSource, isDhisInstance } from "../../domain/instance/entities/DataSource";
42
import { getD2APiFromInstance } from "../../utils/d2-utils";
53
import { DataStore, DataStoreKey } from "../../domain/metadata/entities/MetadataEntities";
64
import { promiseMap } from "../../utils/common";
75
import { DataStoreMetadata } from "../../domain/data-store/DataStoreMetadata";
6+
import { Instance } from "../../domain/instance/entities/Instance";
87

98
export class D2ApiDataStore {
109
private api: D2Api;
1110

12-
constructor(instance: DataSource) {
13-
if (!isDhisInstance(instance)) {
14-
throw new Error("Invalid instance type for MetadataD2ApiRepository");
15-
}
11+
constructor(instance: Instance) {
1612
this.api = getD2APiFromInstance(instance);
1713
}
1814

19-
async getDataStore(filter: { namespaces: string[] }): Promise<DataStore[]> {
15+
async getDataStores(filter: { namespaces?: string[] }): Promise<DataStore[]> {
2016
const response = await this.api.request<string[]>({ method: "get", url: "/dataStore" }).getData();
21-
const namespacesWithKeys = await this.getAllKeysFromNamespaces(response);
22-
return filter.namespaces.length === 0
23-
? namespacesWithKeys
24-
: _(namespacesWithKeys)
25-
.map(namespace => {
26-
return filter.namespaces.includes(namespace.id) ? namespace : undefined;
17+
const namespacesWithKeys = await this.getAllKeysFromNamespaces(
18+
filter.namespaces
19+
? DataStoreMetadata.getDataStoreIds(filter.namespaces || []).map(ns => {
20+
const [namespace] = ns.split(DataStoreMetadata.NS_SEPARATOR);
21+
return namespace;
2722
})
28-
.compact()
29-
.value();
23+
: response
24+
);
25+
return namespacesWithKeys;
3026
}
3127

3228
private async getAllKeysFromNamespaces(namespaces: string[]): Promise<DataStore[]> {
@@ -37,7 +33,7 @@ export class D2ApiDataStore {
3733
displayName: namespace,
3834
externalAccess: false,
3935
favorites: [],
40-
id: `${namespace}${DataStoreMetadata.NS_SEPARATOR}`,
36+
id: [namespace, DataStoreMetadata.NS_SEPARATOR].join(""),
4137
keys: keys,
4238
name: namespace,
4339
translations: [],
@@ -48,8 +44,7 @@ export class D2ApiDataStore {
4844

4945
private async getKeysPaginated(keysState: DataStoreKey[], namespace: string): Promise<DataStoreKey[]> {
5046
const keyResponse = await this.getKeysByNameSpace(namespace);
51-
const newKeys = [...keysState, ...keyResponse];
52-
return newKeys;
47+
return [...keysState, ...keyResponse];
5348
}
5449

5550
private async getKeysByNameSpace(namespace: string): Promise<DataStoreKey[]> {
@@ -68,6 +63,6 @@ export class D2ApiDataStore {
6863
}
6964

7065
private buildArrayDataStoreKey(keys: string[], namespace: string): DataStoreKey[] {
71-
return keys.map(key => ({ id: `${namespace}${DataStoreMetadata.NS_SEPARATOR}${key}`, displayName: key }));
66+
return keys.map(key => ({ id: DataStoreMetadata.generateKeyId(namespace, key), displayName: key }));
7267
}
7368
}

src/data/data-store/DataStoreMetadataD2Repository.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from "lodash";
22
import { DataStoreMetadata } from "../../domain/data-store/DataStoreMetadata";
3-
import { DataStoreMetadataRepository, SaveOptions } from "../../domain/data-store/DataStoreMetadataRepository";
3+
import { DataStoreMetadataRepository } from "../../domain/data-store/DataStoreMetadataRepository";
44
import { Instance } from "../../domain/instance/entities/Instance";
55
import { Stats } from "../../domain/reports/entities/Stats";
66
import { SynchronizationResult } from "../../domain/reports/entities/SynchronizationResult";
@@ -50,11 +50,8 @@ export class DataStoreMetadataD2Repository implements DataStoreMetadataRepositor
5050
return keys.map(key => ({ id: key, value: "" }));
5151
}
5252

53-
async save(
54-
dataStores: DataStoreMetadata[],
55-
options: SaveOptions = { mergeMode: "MERGE" }
56-
): Promise<SynchronizationResult> {
57-
const keysIdsToDelete = await this.getKeysToDelete(dataStores, options);
53+
async save(dataStores: DataStoreMetadata[]): Promise<SynchronizationResult> {
54+
const keysIdsToDelete = await this.getKeysToDelete(dataStores);
5855

5956
const resultStats = await promiseMap(dataStores, async dataStore => {
6057
const dataStoreClient = new StorageDataStoreClient(this.instance, dataStore.namespace);
@@ -91,19 +88,23 @@ export class DataStoreMetadataD2Repository implements DataStoreMetadataRepositor
9188
return result;
9289
}
9390

94-
private async getKeysToDelete(dataStores: DataStoreMetadata[], options: SaveOptions) {
95-
if (options.mergeMode === "MERGE") return [];
91+
private async getKeysToDelete(dataStores: DataStoreMetadata[]): Promise<string[]> {
92+
const allKeysToDelete = await promiseMap(dataStores, async dataStore => {
93+
if (dataStore.action === "MERGE") return [];
9694

97-
const existingRecords = await this.get(dataStores.map(x => ({ ...x, keys: [] })));
98-
const existingKeysIds = existingRecords.flatMap(dataStore => {
99-
return dataStore.keys.map(key => `${dataStore.namespace}[NS]${key.id}`);
100-
});
95+
const existingRecords = await this.get([{ ...dataStore, keys: [] }]);
96+
const existingKeysIds = existingRecords.flatMap(dataStore => {
97+
return dataStore.keys.map(key => DataStoreMetadata.generateKeyId(dataStore.namespace, key.id));
98+
});
99+
100+
const keysIdsToSave = dataStores.flatMap(dataStore => {
101+
return dataStore.keys.map(key => DataStoreMetadata.generateKeyId(dataStore.namespace, key.id));
102+
});
101103

102-
const keysIdsToSave = dataStores.flatMap(dataStore => {
103-
return dataStore.keys.map(key => `${dataStore.namespace}[NS]${key.id}`);
104+
const keysIdsToDelete = existingKeysIds.filter(id => !keysIdsToSave.includes(id));
105+
return keysIdsToDelete;
104106
});
105107

106-
const keysIdsToDelete = existingKeysIds.filter(id => !keysIdsToSave.includes(id));
107-
return keysIdsToDelete;
108+
return allKeysToDelete.flat();
108109
}
109110
}

src/data/metadata/MetadataD2ApiRepository.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,10 @@ export class MetadataD2ApiRepository implements MetadataRepository {
8686
metadataTransformations
8787
);
8888

89-
if (dataStoreIds.length > 0) {
90-
metadataPackage.dataStores = await d2ApiDataStore.getDataStore({ namespaces: ids });
91-
}
89+
const dataStoresMetadata = await this.getDataStoresMetadata(ids);
90+
const responseWithDataStores = { ...metadataPackage, ...dataStoresMetadata } as T;
9291

93-
return metadataPackage as T;
92+
return responseWithDataStores;
9493
} else {
9594
const metadataPackage = this.transformationRepository.mapPackageFrom(
9695
apiVersion,
@@ -99,13 +98,24 @@ export class MetadataD2ApiRepository implements MetadataRepository {
9998
);
10099

101100
if (dataStoreIds.length > 0) {
102-
metadataPackage.dataStores = await d2ApiDataStore.getDataStore({ namespaces: ids });
101+
metadataPackage.dataStores = await d2ApiDataStore.getDataStores({ namespaces: ids });
103102
}
103+
const dataStoresMetadata = await this.getDataStoresMetadata(ids);
104+
const responseWithDataStores = { ...metadataPackage, ...dataStoresMetadata } as T;
104105

105-
return metadataPackage as T;
106+
return responseWithDataStores;
106107
}
107108
}
108109

110+
private async getDataStoresMetadata(ids: Id[]) {
111+
const d2ApiDataStore = new D2ApiDataStore(this.instance);
112+
const dataStoreIds = DataStoreMetadata.getDataStoreIds(ids);
113+
if (dataStoreIds.length === 0) return {};
114+
115+
const dataStores = await d2ApiDataStore.getDataStores({ namespaces: dataStoreIds });
116+
return { dataStores: dataStores };
117+
}
118+
109119
@cache()
110120
public async listMetadata(listParams: ListMetadataParams): Promise<ListMetadataResponse> {
111121
const { type, fields = { $owner: true }, page, pageSize, order, rootJunction, ...params } = listParams;
@@ -115,7 +125,7 @@ export class MetadataD2ApiRepository implements MetadataRepository {
115125
const options = { type, fields, filter, order, page, pageSize, rootJunction };
116126
if (type === "dataStores") {
117127
const d2ApiDataStore = new D2ApiDataStore(this.instance);
118-
const response = await d2ApiDataStore.getDataStore({ namespaces: [] });
128+
const response = await d2ApiDataStore.getDataStores({ namespaces: undefined });
119129
// Hardcoded pagination since DHIS2 does not support pagination for namespaces
120130
return { objects: response, pager: { page: 1, total: response.length, pageSize: 100 } };
121131
} else {

src/domain/data-store/DataStoreMetadata.ts

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
11
import _ from "lodash";
22
import { Maybe } from "../../types/utils";
3+
import { Id } from "../common/entities/Schemas";
34
import { MetadataImportParams } from "../metadata/entities/MetadataSynchronizationParams";
45
import { ObjectSharing } from "../storage/repositories/StorageClient";
56

67
export type DataStoreNamespace = string;
78
export type DataStoreKey = { id: string; value: any };
89

9-
export type DataStoreAttrs = { namespace: DataStoreNamespace; keys: DataStoreKey[]; sharing: Maybe<ObjectSharing> };
10+
export type DataStoreAttrs = {
11+
namespace: DataStoreNamespace;
12+
keys: DataStoreKey[];
13+
sharing: Maybe<ObjectSharing>;
14+
action?: MetadataImportParams["mergeMode"];
15+
};
16+
export type DataStoreOptions = { action: MetadataImportParams["mergeMode"] };
17+
type NamespaceWithAction = { namespace: DataStoreNamespace; options: DataStoreOptions };
1018

1119
export class DataStoreMetadata {
1220
public readonly namespace: DataStoreNamespace;
1321
public readonly keys: DataStoreKey[];
1422
public readonly sharing: Maybe<ObjectSharing>;
23+
public readonly action?: MetadataImportParams["mergeMode"];
1524

1625
static NS_SEPARATOR = "[NS]";
1726

1827
constructor(data: DataStoreAttrs) {
1928
this.keys = data.keys;
2029
this.namespace = data.namespace;
2130
this.sharing = data.sharing;
31+
this.action = data.action;
2232
}
2333

2434
static buildFromKeys(keysWithNamespaces: string[]): DataStoreMetadata[] {
@@ -42,10 +52,7 @@ export class DataStoreMetadata {
4252
return new DataStoreMetadata({
4353
namespace,
4454
keys: _(keys)
45-
.map(key => {
46-
if (!key.key) return undefined;
47-
return { id: key.key, value: "" };
48-
})
55+
.map(key => (key.key ? { id: key.key, value: "" } : undefined))
4956
.compact()
5057
.value(),
5158
sharing: undefined,
@@ -57,20 +64,25 @@ export class DataStoreMetadata {
5764
}
5865

5966
static combine(
67+
ids: Id[],
6068
origin: DataStoreMetadata[],
6169
destination: DataStoreMetadata[],
62-
action: MetadataImportParams["mergeMode"] = "MERGE"
70+
options: DataStoreOptions
6371
): DataStoreMetadata[] {
6472
const destinationDataStore = _.keyBy(destination, "namespace");
73+
const namespacesWithAction = this.mergeOrReplaceNameSpace(ids, options);
6574

6675
return _(origin)
6776
.map(originItem => {
77+
const namespaceAction = namespacesWithAction.find(
78+
({ namespace }) => namespace === originItem.namespace
79+
);
6880
const destItem = destinationDataStore[originItem.namespace];
6981

7082
if (!destItem) return originItem;
7183

7284
const combinedKeys =
73-
action === "MERGE"
85+
namespaceAction?.options.action === "MERGE"
7486
? _(originItem.keys)
7587
.unionBy(destItem.keys, record => record.id)
7688
.value()
@@ -80,6 +92,7 @@ export class DataStoreMetadata {
8092
namespace: originItem.namespace,
8193
keys: combinedKeys,
8294
sharing: originItem.sharing,
95+
action: namespaceAction?.options.action,
8396
});
8497
})
8598
.value();
@@ -98,4 +111,30 @@ export class DataStoreMetadata {
98111
static getDataStoreIds(keys: string[]): string[] {
99112
return keys.filter(id => id.includes(DataStoreMetadata.NS_SEPARATOR));
100113
}
114+
115+
static isDataStoreId(id: string): boolean {
116+
return id.includes(DataStoreMetadata.NS_SEPARATOR);
117+
}
118+
119+
static isNamespaceOnlySelected(id: DataStoreNamespace): boolean {
120+
return _(id).split(DataStoreMetadata.NS_SEPARATOR).compact().value().length === 1;
121+
}
122+
123+
static mergeOrReplaceNameSpace(metadataIds: Id[], options: DataStoreOptions): NamespaceWithAction[] {
124+
return _(metadataIds)
125+
.map((id): Maybe<NamespaceWithAction> => {
126+
if (!this.isDataStoreId(id)) return undefined;
127+
const [namespace] = id.split(DataStoreMetadata.NS_SEPARATOR);
128+
if (!namespace) return undefined;
129+
130+
const isNamespaceSelected = this.isNamespaceOnlySelected(id);
131+
return { namespace: namespace, options: { action: isNamespaceSelected ? options.action : "MERGE" } };
132+
})
133+
.compact()
134+
.value();
135+
}
136+
137+
static generateKeyId(namespace: DataStoreNamespace, keyId: string) {
138+
return [namespace, DataStoreMetadata.NS_SEPARATOR, keyId].join("");
139+
}
101140
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Instance } from "../instance/entities/Instance";
2-
import { MetadataImportParams } from "../metadata/entities/MetadataSynchronizationParams";
32
import { SynchronizationResult } from "../reports/entities/SynchronizationResult";
43
import { DataStoreMetadata } from "./DataStoreMetadata";
54

@@ -9,7 +8,5 @@ export interface DataStoreMetadataRepositoryConstructor {
98

109
export interface DataStoreMetadataRepository {
1110
get(namespaces: DataStoreMetadata[]): Promise<DataStoreMetadata[]>;
12-
save(namespaces: DataStoreMetadata[], options: SaveOptions): Promise<SynchronizationResult>;
11+
save(namespaces: DataStoreMetadata[]): Promise<SynchronizationResult>;
1312
}
14-
15-
export type SaveOptions = { mergeMode: MetadataImportParams["mergeMode"] };

0 commit comments

Comments
 (0)