forked from hyperledger-archives/aries-framework-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsonld.go
150 lines (116 loc) · 2.89 KB
/
jsonld.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package verifiable
import (
"errors"
"fmt"
"reflect"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
)
const (
// ContextURI is the required JSON-LD context for VCs and VPs.
ContextURI = "https://www.w3.org/2018/credentials/v1"
// VCType is the required Type for Verifiable Credentials.
VCType = "VerifiableCredential"
// VPType is the required Type for Verifiable Credentials.
VPType = "VerifiablePresentation"
)
func compactJSONLD(doc string, opts *jsonldCredentialOpts, strict bool) error {
docMap, err := toMap(doc)
if err != nil {
return fmt.Errorf("convert JSON-LD doc to map: %w", err)
}
jsonldProc := jsonld.Default()
docCompactedMap, err := jsonldProc.Compact(docMap,
nil, jsonld.WithDocumentLoader(opts.jsonldDocumentLoader),
jsonld.WithExternalContext(opts.externalContext...))
if err != nil {
return fmt.Errorf("compact JSON-LD document: %w", err)
}
if strict && !mapsHaveSameStructure(docMap, docCompactedMap) {
return errors.New("JSON-LD doc has different structure after compaction")
}
return nil
}
func mapsHaveSameStructure(originalMap, compactedMap map[string]interface{}) bool {
original := compactMap(originalMap)
compacted := compactMap(compactedMap)
if reflect.DeepEqual(original, compacted) {
return true
}
if len(original) != len(compacted) {
return false
}
for k, v1 := range original {
v1Map, isMap := v1.(map[string]interface{})
if !isMap {
continue
}
v2, present := compacted[k]
if !present { // special case - the name of the map was mapped, cannot guess what's a new name
continue
}
v2Map, isMap := v2.(map[string]interface{})
if !isMap {
return false
}
if !mapsHaveSameStructure(v1Map, v2Map) {
return false
}
}
return true
}
func compactMap(m map[string]interface{}) map[string]interface{} {
mCopy := make(map[string]interface{})
for k, v := range m {
// ignore context
if k == "@context" {
continue
}
vNorm := compactValue(v)
switch kv := vNorm.(type) {
case []interface{}:
mCopy[k] = compactSlice(kv)
case map[string]interface{}:
mCopy[k] = compactMap(kv)
default:
mCopy[k] = vNorm
}
}
return mCopy
}
func compactSlice(s []interface{}) []interface{} {
sCopy := make([]interface{}, len(s))
for i := range s {
sItem := compactValue(s[i])
switch sItem := sItem.(type) {
case map[string]interface{}:
sCopy[i] = compactMap(sItem)
default:
sCopy[i] = sItem
}
}
return sCopy
}
func compactValue(v interface{}) interface{} {
switch cv := v.(type) {
case []interface{}:
// consists of only one element
if len(cv) == 1 {
return compactValue(cv[0])
}
return cv
case map[string]interface{}:
// contains "id" element only
if len(cv) == 1 {
if _, ok := cv["id"]; ok {
return cv["id"]
}
}
return cv
default:
return cv
}
}